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+ withpodman 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)
- Copy
audithd-1.4.0-onprem.tar.gzto the server and verify it against the checksum SMH Global sent you:sha256sum audithd-1.4.0-onprem.tar.gz - 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" - Run the installer:
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:sudo ./audithd.sh install- creates
.env(permissions 600) with random secrets for the app and the database, - creates a temporary TLS certificate if
certs/is empty, - builds the
audithdimage (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.
- creates
- 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.
- Extract the bundle as in section 3, steps 1–2.
- Put the images file in the bundle's
images/folder:sudo cp audithd-1.4.0-images.tar.gz /opt/audithd/images/ - 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_URLhost 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:
- In
.env, setCOMPOSE_PROFILES=(empty),APP_BIND=0.0.0.0andPUBLIC_URL=https://<your name>. - Point the load balancer at
http://<server>:8080. ForwardX-Forwarded-ForandX-Forwarded-Proto. - Restrict port 8080 at the firewall so that only the load balancer can reach it.
- 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 logfiles.tar.gz: all evidence filesenv.backup: settings and secrets, including the key that decrypts stored AI keyslicense.json,VERSION, andSHA256SUMS
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.
- Install Node.js 22 LTS and PostgreSQL 16 from your approved repositories.
- 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; - 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 - Copy
systemd/audithd.env.exampleto/etc/audithd/audithd.envand fill it in. GenerateAUDITHD_SECRETwithopenssl rand -hex 32. Then:sudo chown root:audithd /etc/audithd/audithd.env && sudo chmod 640 /etc/audithd/audithd.env - 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 - Put nginx (or your existing web tier) in front of it. The included
nginx.confworks if you changeproxy_passtohttp://127.0.0.1:8080. - Back up with
pg_dump -Fc audithdand 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
BYPASSRLSrights; 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 theappservice indocker-compose.yml:logging: driver: syslog options: { syslog-address: "tcp://siem.bank.local:514", tag: "audithd" } - Monitoring.
GET /api/healthreports the version and mode;GET /api/readyalso 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.