AuditHD

Installation and operations guide.

For the information-technology and security teams installing the on-premises edition.

Product: AuditHD 1.4 (audit, quality-review and compliance platform) Vendor: SMH Global Services Audience: the bank's infrastructure, database and information-security teams

This guide covers a standard Docker installation, an offline (air-gapped) installation, and an installation without containers. Commands assume a Linux server and a user with sudo.

Deployment models. The same bundle serves every model; only the licence and two settings differ.

Model Who runs it Data stays Controlled by
SaaS, shared SMH Global, regional data centre SMH Global's regional service; each organisation isolated by row-level security Subscription (set in SMH Global's provider console)
SaaS, dedicated SMH Global, one instance per customer The country the customer chooses Licence file and subscription
Customer cloud The customer, or SMH Global under contract The customer's cloud account or an in-country cloud Licence file
On-premises The customer's IT team The customer's data centre Licence file; renewals delivered online
On-premises, air-gapped The customer's IT team The customer's data centre, no internet connection Licence file and update images delivered on approved media; usage reported by file (section 5)

1. What gets installed

 Browser / mobile (PWA)
        │  HTTPS 443
        ▼
 ┌──────────────┐   HTTP 8080    ┌──────────────┐   5432 (internal only)  ┌───────────────┐
 │ proxy (nginx)│ ─────────────▶ │ app (AuditHD)│ ──────────────────────▶ │ db (PostgreSQL│
 │ TLS 1.2/1.3  │                │ web + API    │                         │ 16)           │
 └──────────────┘                └──────┬───────┘                         └───────────────┘
                                        │ optional
                                        ▼
                                 ┌──────────────┐
                                 │ ollama (local│  on-premises AI model server
                                 │ AI models)   │  (profile "ai-local")
                                 └──────────────┘
Component Container Data kept in
Web app and API app (image audithd:<version>, runs as UID 10001, read-only file system) volume audithd_app-data (evidence files)
Database db (postgres:16-alpine, data checksums on) volume audithd_db-data
HTTPS front door proxy (nginx:stable-alpine) ./certs
Local AI (optional) ollama volume audithd_ollama-models

The database sits on an internal network with no route in or out. The app connects to it as audithd, a role without superuser or BYPASSRLS rights. PostgreSQL row-level security limits every query to one organisation's data, except where the app explicitly runs system-wide tasks such as sign-in and scheduled SLA reminders.

2. Requirements

Server size. These are starting points. Adjust them once you see real load.

Deployment vCPU RAM Disk Notes
Up to 50 users 4 8 GB 100 GB SSD Single server
Up to 300 users 8 16 GB 250 GB SSD Single server; consider an external PostgreSQL
Local AI model (14–32B parameters) +8 +32 GB +60 GB NVIDIA GPU with 24 GB+ VRAM strongly recommended

Evidence files take up most of the disk. Plan for your evidence volume times your retention years.

Software

  • 64-bit Linux: RHEL / Rocky / AlmaLinux 8 or 9, or Ubuntu 22.04 / 24.04 LTS
  • Docker Engine 24+ with the Compose plugin (docker compose version), or Podman 4.7+ with podman compose
  • openssl, tar, sha256sum (present on standard installs)

Network

  • Inbound: TCP 443 from users (TCP 80 is optional; it only redirects to 443)
  • A DNS name for the server, e.g. audithd.bank.local
  • A TLS certificate from the bank's CA. Until you install one, a temporary self-signed certificate is created.
  • Outbound internet is not required. It is needed only for the cloud AI providers you choose to enable, and for pulling images in an online install.
  • Optional: an SMTP relay for e-mail notifications.

3. Standard installation (server has internet access)

  1. Copy audithd-1.4.0-onprem.tar.gz to the server and verify it against the checksum SMH Global sent you:
    sha256sum audithd-1.4.0-onprem.tar.gz
    
  2. Extract it to /opt:
    sudo tar -xzf audithd-1.4.0-onprem.tar.gz -C /opt
    sudo mv /opt/audithd-1.4.0-onprem /opt/audithd
    cd /opt/audithd
    sha256sum -c SHA256SUMS --quiet && echo "Bundle intact"
    
  3. Run the installer:
    sudo ./audithd.sh install
    
    It asks for the server name, the organisation name, the region, and the first administrator's name, e-mail and password. Press Enter to generate a strong password. The installer then:
    • creates .env (permissions 600) with random secrets for the app and the database,
    • creates a temporary TLS certificate if certs/ is empty,
    • builds the audithd image (about one minute; only the Node.js base image is downloaded),
    • starts the stack, waits until it is healthy, and creates the organisation and administrator,
    • removes the administrator password from .env.
  4. Open the address it prints and sign in.

Unattended installation (for automation):

sudo AUDITHD_HOST=audithd.bank.local AUDITHD_ORG="Example Bank" AUDITHD_REGION=SA \
     AUDITHD_ADMIN_NAME="Audit Admin" AUDITHD_ADMIN_EMAIL=audit.admin@bank.local \
     AUDITHD_ADMIN_PASSWORD='S0me-Strong-Passw0rd' ./audithd.sh install --yes

4. Offline (air-gapped) installation

SMH Global supplies a second file, audithd-1.4.0-images.tar.gz. It contains the prebuilt AuditHD image plus the PostgreSQL and nginx images.

  1. Extract the bundle as in section 3, steps 1–2.
  2. Put the images file in the bundle's images/ folder:
    sudo cp audithd-1.4.0-images.tar.gz /opt/audithd/images/
    
  3. Run sudo ./audithd.sh install. The installer loads the images first, so it never contacts a registry.

To produce the images file yourself on a machine with internet access, run this from the source tree: scripts/make-release.sh --with-images.

5. After the first sign-in

Step Where
Change the administrator password User menu (top right) → Change password
Install the licence file from SMH Global sudo ./audithd.sh license /path/license.json
Install the bank's TLS certificate sudo ./audithd.sh cert fullchain.pem private-key.pem (section 6)
Add departments and users, and assign roles Administration → Users and Org units
Set response and remediation SLAs, report branding, retention Administration → SLA and branding
Choose the AI policy and provider Administration → AI settings (section 10)
Configure e-mail Set SMTP_URL and SMTP_FROM in .env, then sudo ./audithd.sh restart
Schedule backups Section 7

Licence. Without a licence file AuditHD runs in evaluation mode (International standards only, no country packs, up to 10 licensed users per organisation), which is not for production use. A licence limits licensed users: administrators, chief audit executives, managers, auditors, QA reviewers and risk officers. Auditees and viewers are unlimited. The licence is an Ed25519-signed file and works fully offline. It can also:

  • list the modules you subscribe to (audit management is always included; risk and control self-assessment, AI review and the REST API are optional);
  • list the country packs you license: Pakistan, Saudi Arabia, United Arab Emirates, Bahrain, Qatar, Kuwait, Oman, European Union, United States. Each pack brings that jurisdiction's regulatory frameworks, checklists, obligations calendar, retention periods and reporting conventions; International standards (ISO, CMMI, NIST CSF and similar) are included with every licence. Frameworks and checklists of other packs stay visible in the Library, locked. A licence issued before version 1.5 lists no packs and covers every pack;
  • be a term licence (it expires) or a perpetual licence with a maintenance end date. A perpetual licence keeps working after maintenance ends; a version released after that date shows a renewal reminder under Administration → About;
  • be bound to your server's address (PUBLIC_URL host name), if your contract says so.

Install or replace it with sudo ./audithd.sh license /path/license.json. Check it under Administration → About and Administration → Subscription.

Usage report. For renewals and true-ups SMH Global asks for a usage report: the number of licensed users, auditees, engagements, AI reviews and storage per organisation. It contains no audit content and carries a checksum. Export it with sudo ./audithd.sh usage-report (whole installation) or, as an administrator, under Administration → Subscription → Download usage report (your organisation). Air-gapped sites send the file on approved media.

6. TLS certificate

sudo ./audithd.sh cert /path/fullchain.pem /path/private-key.pem

The command checks that the certificate and key belong together, installs them, and reloads the proxy. The key must not be password-protected. The proxy allows only TLS 1.2 and 1.3 with forward-secret ciphers and sends HSTS.

Using your own load balancer (for example F5 or Citrix ADC) to terminate TLS:

  1. In .env, set COMPOSE_PROFILES= (empty), APP_BIND=0.0.0.0 and PUBLIC_URL=https://<your name>.
  2. Point the load balancer at http://<server>:8080. Forward X-Forwarded-For and X-Forwarded-Proto.
  3. Restrict port 8080 at the firewall so that only the load balancer can reach it.
  4. Run sudo ./audithd.sh start.

7. Backup and restore

sudo ./audithd.sh backup                 # to /opt/audithd/backups
sudo ./audithd.sh backup /mnt/backup     # to another folder

Each backup is a folder audithd-YYYYMMDD-HHMMSS with these files:

  • database.dump: the full PostgreSQL dump, including the hash-chained audit log
  • files.tar.gz: all evidence files
  • env.backup: settings and secrets, including the key that decrypts stored AI keys
  • license.json, VERSION, and SHA256SUMS

Backups older than 30 days in the same folder are deleted automatically. Change this with BACKUP_KEEP_DAYS=90 ./audithd.sh backup, or set 0 to keep everything.

Nightly schedule (root crontab, sudo crontab -e):

30 1 * * * cd /opt/audithd && ./audithd.sh backup /mnt/backup/audithd >> /var/log/audithd-backup.log 2>&1

Copy backups to encrypted off-server storage. They contain confidential audit data and secrets.

Restore. This replaces all current data:

sudo ./audithd.sh restore /mnt/backup/audithd/audithd-20261026-013000

The command verifies the checksums, asks you to type RESTORE, and then reloads the database and files. After a restore, open Administration → Audit trail → Verify integrity to confirm the audit trail is complete and unaltered. Test a restore on a separate server at least once a quarter.

8. Upgrades

cd /opt/audithd
sudo rm -rf app                                        # old program files only; data is in volumes
sudo tar -xzf ~/audithd-1.4.0-onprem.tar.gz -C /opt/audithd --strip-components=1
sudo ./audithd.sh upgrade

upgrade takes a backup, builds or loads the new image, restarts the app, and waits until it is healthy. Database changes are applied automatically at start-up. They run under a lock, so they run exactly once even with several app instances. Your .env, license/, certs/ and backups/ are not touched. For offline servers, put the new images file in images/ before running upgrade.

Upgrading from Auditra 1.0.0. AuditHD is the new name of Auditra. Version 1.0.0 was installed as /opt/auditra with the auditra.sh script, so it moves to a new folder once, with a backup and restore:

cd /opt/auditra && sudo ./auditra.sh backup                  # note the folder it prints, e.g. backups/auditra-20261027-020000
sudo tar -xzf ~/audithd-1.4.0-onprem.tar.gz -C /opt
sudo mv /opt/audithd-1.4.0-onprem /opt/audithd
cd /opt/audithd && sudo ./audithd.sh install                 # same server name and administrator e-mail as before
sudo cp /opt/auditra/license/license.json /tmp/ && sudo ./audithd.sh license /tmp/license.json
sudo ./audithd.sh restore /opt/auditra/backups/auditra-20261027-020000
cd /opt/auditra && sudo ./auditra.sh down                    # stops the old copy; its data volumes stay until you remove them

restore accepts Auditra 1.0.0 backups: the data is re-owned by the new database account automatically. Sign in with your existing accounts, then check Administration → Audit trail → Verify integrity. Move any auditra.sh backup line in the root crontab to audithd.sh.

9. Installation without containers (systemd)

Use this method if containers are not permitted.

  1. Install Node.js 22 LTS and PostgreSQL 16 from your approved repositories.
  2. Create the database role and database. Choose your own password:
    CREATE ROLE audithd LOGIN PASSWORD '...' NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS;
    CREATE DATABASE audithd OWNER audithd ENCODING 'UTF8' TEMPLATE template0;
    REVOKE ALL ON DATABASE audithd FROM PUBLIC;
    \c audithd
    REVOKE CREATE ON SCHEMA public FROM PUBLIC;
    GRANT ALL ON SCHEMA public TO audithd;
    
  3. Create the service account and folders, and copy the program:
    sudo useradd --system --home /var/lib/audithd --shell /usr/sbin/nologin audithd
    sudo mkdir -p /opt/audithd /var/lib/audithd /etc/audithd
    sudo cp -r app /opt/audithd/ && sudo cp INSTALL.md /opt/audithd/
    sudo chown audithd:audithd /var/lib/audithd && sudo chmod 750 /var/lib/audithd
    
  4. Copy systemd/audithd.env.example to /etc/audithd/audithd.env and fill it in. Generate AUDITHD_SECRET with openssl rand -hex 32. Then:
    sudo chown root:audithd /etc/audithd/audithd.env && sudo chmod 640 /etc/audithd/audithd.env
    
  5. Install and start the service:
    sudo cp systemd/audithd.service /etc/systemd/system/
    sudo systemctl daemon-reload && sudo systemctl enable --now audithd
    curl -s http://127.0.0.1:8080/api/health
    
  6. Put nginx (or your existing web tier) in front of it. The included nginx.conf works if you change proxy_pass to http://127.0.0.1:8080.
  7. Back up with pg_dump -Fc audithd and an archive of /var/lib/audithd, using the same schedule as section 7.

10. AI options

AuditHD works fully without a language model: its rules engine checks responses, finding quality and checklists. Language models add drafting, review comments, checklist generation and Arabic translation. An auditor approves every AI suggestion, and each one is logged.

Set the provider under Administration → AI settings. The hosting policy controls where data may go:

Policy Allowed providers Typical use
Off none strictest environments
On-premises only OpenAI-compatible endpoints on private addresses only (Ollama, vLLM) banks with no external data transfer
In-country cloud a provider hosted in your country with an OpenAI-compatible API data-residency rules
Any approved Anthropic Claude, OpenAI, Azure OpenAI, or any of the above where cloud processing is approved

Local model with Ollama (keeps all data on the server):

sudo ./audithd.sh ai-pull qwen3:14b

Then in Administration → AI settings, choose provider OpenAI-compatible endpoint, endpoint http://ollama:11434/v1, model qwen3:14b, and policy On-premises only. For a GPU, uncomment the deploy: block of the ollama service in docker-compose.yml. A vLLM server elsewhere on your network works the same way: use its URL as the endpoint.

Cloud providers. Enter the API key in Administration → AI settings. Keys are encrypted with AES-256-GCM using AUDITHD_SECRET. Alternatively, set ANTHROPIC_API_KEY or OPENAI_API_KEY in .env. With PII redaction on (the default for cloud providers), AuditHD removes IBANs, card numbers, national IDs, phone numbers and e-mail addresses before sending text.

Confirm with your compliance function before you enable any provider outside your country. For example, SAMA's Cyber Security Framework and outsourcing rules require approval for cloud services and for data processed outside Saudi Arabia.

11. Security notes for the information-security review

  • Containers. They run without root, with no Linux capabilities, a read-only file system, no-new-privileges, and log rotation. The database has no published port and no outbound route.
  • Data isolation. PostgreSQL row-level security is forced on every business table, so an application bug cannot leak another organisation's data. The app's role has no superuser or BYPASSRLS rights; only explicit system tasks (sign-in, scheduled jobs) run unscoped. Treat the database password as a privileged credential.
  • Audit trail. The log is append-only and SHA-256 hash-chained, and a database trigger blocks updates and deletes. You can verify the whole chain from the app.
  • Authentication. Passwords are hashed with bcrypt (cost 12) and must be at least 12 characters with mixed character types. After 5 failed attempts the account locks for 15 minutes. Sessions are HTTP-only, SameSite cookies and expire after 10 hours (SESSION_HOURS). Changing or resetting a password signs out the user's other sessions. CSRF is blocked by a required request header. Login and setup are rate-limited.
  • Transport. TLS 1.2/1.3 only, with HSTS. The app sets a strict Content-Security-Policy.
  • Secrets. They live in .env (mode 600). Stored API keys are encrypted with AES-256-GCM.
  • Evidence integrity. Every file's SHA-256 is recorded at upload and checked on download.
  • Encryption at rest. Put /var/lib/docker (or the volumes) on an encrypted file system such as LUKS or encrypted SAN storage, as your standards require.
  • SIEM. Security events (login, login_failed, logout, password_change, access_denied, setup) are written to the app log as JSON with "security":true. To forward all app logs to syslog, add this to the app service in docker-compose.yml:
    logging:
      driver: syslog
      options: { syslog-address: "tcp://siem.bank.local:514", tag: "audithd" }
    
  • Monitoring. GET /api/health reports the version and mode; GET /api/ready also checks the database. Both are unauthenticated and contain no data.

12. Troubleshooting

Symptom Check
install stops at "Building the AuditHD image" The server cannot reach Docker Hub. Use the offline images file (section 4) or a registry mirror.
Browser warns about the certificate The temporary certificate is still installed. Install the bank's certificate (section 6).
App keeps restarting sudo ./audithd.sh logs app. "AUDITHD_SECRET must be set" means .env is incomplete. Database errors usually mean db is still initialising on first start.
"Too many failed attempts" Wait 15 minutes, or ask an administrator to reset the password.
Licence shows "invalid" (Administration → About) The file was edited or belongs to another build. Ask SMH Global for a new one.
AI shows "rules engine" only Administration → AI settings → Test connection. The message explains what is missing, such as a key, an endpoint, or the policy.
Disk filling up Check the evidence volume (docker system df -v) and backups/ retention.

Contact SMH Global Services support with the output of sudo ./audithd.sh status and of sudo ./audithd.sh logs app > audithd-app.log (when redirected to a file, logs saves the last 500 lines instead of following).

13. Reference

Commands: ./audithd.sh install | start | stop | restart | status | logs | backup | restore | upgrade | cert | license | usage-report | ai-pull | load-images | down

Files in the bundle

Path Purpose
app/ Prebuilt AuditHD program (server and web app)
Dockerfile Builds the runtime image from app/ (no source build)
docker-compose.yml The service definitions
.env.example → .env Settings and secrets
nginx.conf, certs/ HTTPS proxy and certificates
postgres-init.sh Creates the restricted database role on first start
license/ Licence file location (mounted read-only)
provider/ Used only by SaaS operators (provider console signing key); leave empty
images/ Put offline image archives here
systemd/ Files for installation without containers

Main settings in .env: PUBLIC_URL, AUDITHD_MODE (onprem, or saas for a hosted service run from the provider console), SUPPORT_EMAIL, COMPOSE_PROFILES, APP_BIND, APP_PORT, HTTPS_PORT, SMTP_URL, SMTP_FROM, ANTHROPIC_API_KEY, OPENAI_API_KEY, OPENAI_BASE_URL, SESSION_HOURS, SLA_INTERVAL_MINUTES, FILE_STORAGE (disk or db), TZ, LOG_LEVEL. Run ./audithd.sh restart after changing any of them.