# Setting Up a New Open Clinical History Server

This guide describes how to deploy a new Open Clinical History installation.

Open Clinical History includes a temporary first-run setup assistant:

```text
setup_delete_after_setup.php

```

The setup assistant is deliberately self-contained so that it can run **before `config.php` exists and before the application database has been configured**.

It guides the administrator through:

```text
Server preflight
      |
      v
Private config.php
      |
      v
Database
      |
      v
Application settings
      |
      v
SNOMED CT
      |
      v
Image/SNOMED database
      |
      v
Queue workers
      |
      v
First patient
      |
      v
Delete setup installer

```

> **Important:** `setup_delete_after_setup.php` must be deleted from the web server after installation is complete.

---

# Requirements

A new installation requires:

- PHP 8.3 or later
- PHP CLI
- MariaDB or MySQL
- Apache, Nginx or another suitable web server
- cURL support
- PDO / PDO MySQL
- mbstring
- JSON support
- `proc_open()`
- a private configuration directory
- a writable runtime directory
- a private SNOMED CT storage directory
- access to the Open Clinical History source repository

The existing installation design separates the private configuration, runtime and SNOMED files from the public application files.

HTTPS is strongly recommended and should be considered mandatory for an installation that will contain real patient information.

---

# Recommended Directory Structure

Sensitive files should remain outside the web-accessible application directory.

For example:

```text
/var/www/
├── private/
│   └── demo/
│       ├── config.php
│       ├── config_template.php
│       ├── health_history.sql
│       ├── run/
│       └── snomed/
│
└── openclinicalhistory/
    └── application files

```

For example:

```bash
root@server:/var/www/private/demo# ls

config.php
config_template.php
health_history.sql
run
snomed

```

The application web directory is public.

The following are **not** public:

```text
config.php
database schema/backups
runtime state
SNOMED CT RF2 files

```

The `/var/www/private` directory must never be configured as the Apache or Nginx document root.

---

# 1. Obtain the Open Clinical History Source

The Open Clinical History source repository is currently private.

Repository:

```text
https://github.com/bengoudieparkoch/openclinicalhistory

```

To request access, visit:

```text
https://www.openclinicalhistory.org/access.php

```

and complete the access request form.

The existing deployment documentation also identifies repository access as an approved-user process rather than a public source download.

After access has been granted, clone the repository into the web application directory.

For example:

```bash
cd /var/www

git clone https://github.com/bengoudieparkoch/openclinicalhistory.git

```

For later application updates:

```bash
cd /var/www/openclinicalhistory

git pull

```

Do not store GitHub credentials in the Open Clinical History source code.

---

# 2. Configure the Web Server

Create an Apache VirtualHost or equivalent Nginx configuration for the application.

For example:

```text
https://clinical.example.org/

```

The web server's document root should point directly to the Open Clinical History application directory.

For example:

```apache
DocumentRoot /var/www/openclinicalhistory

```

Do **not** use:

```apache
DocumentRoot /var/www

```

because that could make `/var/www/private` reachable through the web server.

The original installation guide explicitly separates the application directory from `/var/www/private` for this reason.

---

# 3. Configure HTTPS

Configure SSL/TLS before using Open Clinical History with real clinical information.

Open Clinical History may contain:

- patient identifiers
- dates of birth
- clinical records
- complete medical histories
- API bearer tokens

These should not be transmitted over plain HTTP.

For example:

```text
https://clinical.example.org/

```

The Patient Record API also defaults to requiring HTTPS.

For Apache deployments, Let's Encrypt and Certbot can be used where appropriate.

---

# 4. Open the First-Run Setup Assistant

Once the application files are available through the web server, open:

```text
/setup_delete_after_setup.php

```

For example:

```text
https://clinical.example.org/setup_delete_after_setup.php

```

If Open Clinical History is installed under a subdirectory such as `/demo`, use:

```text
https://clinical.example.org/demo/setup_delete_after_setup.php

```

The setup assistant contains the installation sequence:

```text
0 · Preflight
1 · config.php
2 · Database
3 · App settings
4 · SNOMED
5 · Image DB
6 · Workers
7 · Patient import
8 · Finish

```

It is safe for this page to run before the database is working because it deliberately does not depend on the normal application bootstrap.

---

# 5. Run the Server Preflight

Select:

**0 · Preflight**

The installer checks the web-server PHP environment.

Checks currently include:

<table id="bkmrk-check-requirement-ph"><thead><tr><th>Check</th><th>Requirement</th></tr></thead><tbody><tr><td>PHP version</td><td>PHP 8.3+</td></tr><tr><td>PDO</td><td>Required</td></tr><tr><td>PDO MySQL</td><td>Required</td></tr><tr><td>cURL</td><td>Required</td></tr><tr><td>mbstring</td><td>Required</td></tr><tr><td>JSON</td><td>Required</td></tr><tr><td>`proc_open()`</td><td>Required</td></tr><tr><td>CLI PHP</td><td>Required</td></tr><tr><td>Config template</td><td>Should be available</td></tr></tbody></table>

The setup assistant searches for a CLI PHP executable in this order:

```text
/usr/bin/php8.5
/usr/bin/php8.4
/usr/bin/php8.3
/usr/bin/php

```

A working CLI PHP installation is important because long-running Open Clinical History operations execute outside the browser request.

These include background processing such as:

```text
history_job.php
queue_worker.php
SNOMED import workers

```

---

# Additional Preflight Checks

Also verify manually that:

- MariaDB/MySQL is installed and reachable
- HTTPS is working
- the web-server/worker user can read the application
- the web-server/worker user can read the SNOMED directory
- the runtime directory is writable
- the supplied anatomical PNG catalogue exists under `img/`

Do not proceed until the PHP preflight is clean.

---

# 6. Create the Private Directory

Create the private installation directory.

For example:

```bash
sudo install -d -o root -g www-data -m 0750 /var/www/private/demo

```

Create the writable runtime directory:

```bash
sudo install -d -o www-data -g www-data -m 0770 /var/www/private/demo/run

```

Create the SNOMED storage directory if it does not already exist:

```bash
mkdir -p /var/www/private/demo/snomed

```

The resulting structure should resemble:

```text
/var/www/private/demo/
├── config.php
├── config_template.php
├── health_history.sql
├── run/
└── snomed/

```

The existing setup design uses `run` for background process state and `snomed` for the RF2 terminology release.

---

# 7. Generate `config.php`

Select:

**1 · config.php**

The setup assistant provides an interactive `config.php` generator.

The form is processed entirely in the browser.

Database credentials entered into this setup form are **not posted back to `setup_delete_after_setup.php`**.

Enter:

- PHP CLI binary
- private runtime directory
- database host
- database port
- database name
- database username
- database password
- database character set
- debug mode

The default runtime directory is:

```text
/var/www/private/demo/run

```

The recommended configuration location is:

```text
/var/www/private/demo/config.php

```

---

# Example `config.php`

A typical configuration is:

```php
<?php

return [

    'php_binary' => '/usr/bin/php8.3',

    'run_dir' => '/var/www/private/demo/run',

    'db' => [

        'host'     => '127.0.0.1',

        'port'     => 3306,

        'database' => 'health_history',

        'username' => 'openclinicalhistory',

        'password' => 'your_password',

        'charset'  => 'utf8mb4',

    ],

    'debug' => false,

];

```

The existing guide uses the same private configuration structure and keeps `config.php` outside version control.

---

# PHP CLI Binary

For example:

```php
'php_binary' => '/usr/bin/php8.3',

```

Check it with:

```bash
/usr/bin/php8.3 -v

```

And verify the important extensions:

```bash
/usr/bin/php8.3 -m | grep -E 'pdo_mysql|curl|mbstring'

```

The CLI PHP installation can differ from the PHP installation used by Apache or PHP-FPM.

Both must be correctly configured.

---

# Runtime Directory

For example:

```php
'run_dir' => '/var/www/private/demo/run',

```

This directory stores runtime information such as:

- job status
- locks
- worker state
- processing state

It must be writable by the web/worker account.

---

# Database Connection

For example:

```php
'db' => [

    'host'     => '127.0.0.1',

    'port'     => 3306,

    'database' => 'health_history',

    'username' => 'openclinicalhistory',

    'password' => 'your_password',

    'charset'  => 'utf8mb4',

],

```

Use:

```text
utf8mb4

```

for the database character set.

Clinical records can contain the full Unicode character set.

---

# Database User

Although a `root` database login may be convenient during development, a dedicated application account is recommended.

For example:

```sql
CREATE USER 'openclinicalhistory'@'localhost'
IDENTIFIED BY 'use-a-strong-password';

GRANT ALL PRIVILEGES
ON health_history.*
TO 'openclinicalhistory'@'localhost';

FLUSH PRIVILEGES;

```

Then use that account in `config.php`.

---

# Debug Mode

Production installations should normally use:

```php
'debug' => false,

```

Debug information can expose application or database information and should only be enabled temporarily when troubleshooting.

---

# Save and Protect `config.php`

The setup page allows the generated configuration to be:

- copied
- downloaded as `config.php`

Save it as:

```text
/var/www/private/demo/config.php

```

Then protect it.

For example:

```bash
sudo chown root:www-data /var/www/private/demo/config.php

sudo chmod 640 /var/www/private/demo/config.php

```

The web server needs to read the file.

It should not be writable by the web process.

Do not commit `config.php` to Git. The existing installation guide specifically keeps server credentials and paths outside the repository.

---

# 8. Create the Database

Select:

**2 · Database**

Create the application database.

For example:

```sql
CREATE DATABASE health_history
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

```

The base database schema is supplied with the Open Clinical History installation.

For example:

```text
health_history.sql

```

Import it:

```bash
mysql -u openclinicalhistory -p health_history \
  < /var/www/private/demo/health_history.sql

```

The existing installation article already requires loading this base schema before the application can operate.

---

# Run All Database Migrations

The base schema is not necessarily the end of database setup.

Run **all migrations supplied with the installed Open Clinical History release, in release order**.

The current setup assistant specifically identifies migrations responsible for features including:

```text
app_config
api_token
ingest_queue

```

Known migrations referenced by the current application include:

```text
migrations/2026-08-21_config_and_api_tokens.sql
migrations/2026-08-21b_ingest_queue.sql

```

The release package itself should be treated as authoritative.

If additional migrations are supplied, apply those as well.

Do not skip migrations simply because the base application page loads.

---

# Verify the Database

Connect to the database:

```bash
mysql -u openclinicalhistory -p health_history

```

Then check:

```sql
SHOW TABLES;

```

In particular, before moving to application configuration, confirm that the required configuration and queue tables exist.

---

# 9. Configure Open Clinical History

Select:

**3 · App settings**

or open:

```text
/admin_config.php

```

`config.php` contains the infrastructure settings needed to connect to the server and database.

Operational application settings are instead stored in:

```text
app_config

```

Configure at least:

- date format
- SNOMED RF2 release path
- LLM enabled
- Gemini API key
- Gemini model
- provider limits
- extraction limits
- grounded SNOMED settings
- API configuration
- ingest queue configuration

The existing server article already places these settings after database setup and before SNOMED processing.

---

# Gemini API Key vs Open Clinical History API Token

These are two different credentials.

## Gemini API key

Used for:

```text
Open Clinical History
        |
        v
Gemini

```

It authenticates **outbound LLM requests**.

## Open Clinical History API token

Used for:

```text
External clinical system
        |
        v
Open Clinical History Patient Record API

```

It authenticates **inbound patient-document submissions**.

Do not confuse the two.

---

# API Token

If another system will send patient records through:

```text
history_api_import.php

```

create an API token under:

**Admin → Configuration**

For a normal importing integration, the likely scopes are:

```text
import
status
commit

```

Store the token immediately.

The plaintext token is displayed only once.

Only its hash is stored afterwards.

Enable the import API only after HTTPS has been configured.

---

# 10. Install SNOMED CT

Select:

**4 · SNOMED**

Open Clinical History does not redistribute SNOMED CT.

The installing organisation must obtain the authorised terminology release under the appropriate SNOMED licensing arrangements.

For an Australian installation, obtain the appropriate **SNOMED CT-AU RF2 Snapshot** release.

Store the extracted files in the protected server directory.

For example:

```text
/var/www/private/demo/snomed

```

A release may contain:

```text
Full/
Snapshot/
Delta/

```

with directories such as:

```text
Terminology/
Refset/

```

The previous server guide already specifies the Snapshot release as the source for normal Open Clinical History imports.

---

# Configure the SNOMED Path

Under:

**Admin → Configuration → SNOMED CT RF2**

set:

```text
/var/www/private/demo/snomed

```

or the root of the specific extracted release.

---

# Run SNOMED Import

Open:

**SNOMED Import**

or:

```text
/snomed_import.php

```

Confirm that the RF2 inventory detects:

```text
Concept Snapshot
Description Snapshot
Relationship Snapshot
Language Snapshot

```

Then run:

**Start or resume pipeline**

The original installation guide also requires all four datasets before proceeding.

---

# SNOMED Import Can Take a Long Time

SNOMED CT is large.

The import involves millions of terminology rows and multiple derived-index stages.

Do not proceed simply because the first RF2 tables have been populated.

Wait until the entire SNOMED pipeline reports completion.

The setup assistant deliberately requires the administrator to confirm:

```text
SNOMED import completed and verified

```

before enabling the Image DB step.

---

# 11. Build the Image / SNOMED Database

Select:

**5 · Image DB**

or open:

```text
/build_image_db.php

```

Do this **only after SNOMED import has completely finished**.

The image build combines:

```text
Completed SNOMED terminology
        +
Installed anatomical PNG catalogue
        |
        v
Image / SNOMED Summary Build
        |
        v
Clinical concept ↔ anatomy ↔ image mappings

```

Run:

**Clean rebuild image summary database**

The existing installation sequence also places this operation immediately after SNOMED import.

---

# Verify the Image Build

Review the resulting coverage, including:

- active PNG layers
- SNOMED seed rows
- anatomy hierarchy rows
- concept/image rows
- mapped clinical concepts
- anatomical layers without verified SNOMED seeds

Do not proceed to production patient ingestion until the result looks sensible.

---

# 12. Configure Persistent Queue Workers

Select:

**6 · Workers**

Queued API ingestion requires:

```text
queue_worker.php

```

to be running.

Without a queue worker, records can enter:

```text
ingest_queue

```

but will remain there waiting for processing.

For testing:

```bash
cd /var/www/openclinicalhistory

/usr/bin/php8.3 queue_worker.php

```

The existing server documentation also recommends running the queue worker under a persistent service manager rather than relying on a terminal session.

---

# Use a Service Manager

For production, use a host-level process manager such as:

- systemd
- Supervisor
- another appropriate service manager

Workers should restart automatically following:

- process crashes
- application failures
- server reboots

Do not rely on a browser connection to keep a queue worker alive.

---

# Worker Count

The setup assistant recommends starting conservatively and tuning worker concurrency according to:

- CPU capacity
- Gemini concurrency
- configured LLM budgets
- database load
- available memory

More workers are not automatically better.

The LLM provider, database and token budget can become the limiting resources before CPU does.

---

# Monitor Workers

Open:

```text
/import_monitor.php

```

Confirm that the expected worker processes are visible before proceeding.

---

# 13. Import the First Patient

Select:

**7 · Patient import**

Do not begin with bulk production data.

The recommended first validation is a single known:

```text
synthetic

```

or:

```text
de-identified

```

patient history.

Open:

```text
/history_import.php

```

Upload one plain-text history and allow it to complete.

---

# Validate the Entire Pipeline

For the first patient:

1. Upload the source history.
2. Start extraction.
3. Watch processing through the available monitoring screens.
4. Review the extracted clinical events.
5. Review SNOMED CT classifications.
6. Review anatomical mappings.
7. Check the original source evidence.
8. Review date precision and chronology.
9. Review the audit output.
10. Confirm unresolved terminology appears in the Learning Queue instead of being silently guessed.
11. Complete the import.
12. Open the resulting patient history.

The existing guide already uses manual import as the end-to-end validation of database, worker, Gemini, SNOMED and anatomy configuration.

---

# 14. Test the Patient Record API

Only after the manual test has succeeded should an external integration be enabled.

Use:

```text
/history_api_import.php?action=ping

```

with an Open Clinical History Bearer token.

Then test one non-production record through the API.

Verify:

```text
submission
    |
    v
queued
    |
    v
extracting
    |
    v
audit / commit
    |
    v
patient history

```

Do not expose the API token in:

- browser-side JavaScript
- public source code
- Git repositories
- documentation

The original article similarly places API testing after manual patient validation.

---

# 15. Finish the Installation

Select:

**8 · Finish**

Before removing the setup assistant, confirm:

- private `config.php` is outside the web root
- `config.php` permissions are restricted
- base database schema is installed
- all database migrations are installed
- Gemini/application settings are configured
- SNOMED CT has completed and verified
- Image/SNOMED database has completed and verified
- queue workers are supervised and running
- a test patient completed successfully end-to-end
- the API has been tested if it will be used
- backups have been configured

---

# 16. Delete `setup_delete_after_setup.php`

This step is mandatory.

The first-run installer exposes information useful during server configuration and is intentionally not part of the normal running application.

Once installation has been proven:

```bash
cd /var/www/openclinicalhistory

rm setup_delete_after_setup.php

```

If your application is installed somewhere else, adjust the path accordingly.

For example:

```bash
cd /var/www/openclinicalhistory/demo

rm setup_delete_after_setup.php

```

Confirm that:

```text
https://your-domain.example/setup_delete_after_setup.php

```

now returns:

```text
404 Not Found

```

or is otherwise inaccessible.

> **Open Clinical History does not depend on this file after installation.**

Do not leave it available on a production server.

---

# Updating Open Clinical History Later

After the initial installation:

```bash
cd /var/www/openclinicalhistory

git pull

```

Review the release for:

- database migrations
- changed configuration requirements
- SNOMED rebuild requirements
- Image/SNOMED rebuild requirements
- worker changes

Do not assume that every application update requires a SNOMED or image rebuild.

Follow the release-specific instructions.

If `setup_delete_after_setup.php` is restored by a later Git update, **do not leave it publicly accessible simply because the server is already configured**.

Remove it again unless it is deliberately required for a controlled upgrade procedure.

---

# Recommended File Permissions

Conceptually:

```text
Application source
    read by web server

config.php
    root owned
    readable by web-server group
    not writable by web server

run/
    readable/writable by workers

snomed/
    readable by application/workers
    not web accessible

```

For example:

```bash
sudo chown root:www-data /var/www/private/demo/config.php
sudo chmod 640 /var/www/private/demo/config.php

```

The existing deployment guide also recommends giving the web process write access only where required rather than across the whole application.

---

# Backups

Before using Open Clinical History for important patient information, configure regular backups.

At minimum, protect:

- Open Clinical History database
- private `config.php`
- application-specific customisations
- anatomical image catalogue

The original deployment guidance also recommends retaining the exact SNOMED release where practical because it simplifies recovery and auditing.

Do not rely on Git as a patient-data or database backup.

---

# Security Checklist

A production installation should include:

- HTTPS only
- dedicated database credentials
- restrictive filesystem permissions
- `/var/www/private` inaccessible through HTTP
- no secrets committed to Git
- protected administration pages
- secure API bearer tokens
- current operating-system security updates
- database backups
- appropriate firewall restrictions
- supervised background workers
- deletion of `setup_delete_after_setup.php`

These controls build on the security requirements already identified in the server guide.

---

# New Server Checklist

- [ ]  [ ]  Obtain repository access
- [ ]  [ ]  Clone Open Clinical History
- [ ]  [ ]  Configure Apache/Nginx
- [ ]  [ ]  Configure HTTPS
- [ ]  [ ]  Open `setup_delete_after_setup.php`
- [ ]  [ ]  Pass server preflight
- [ ]  [ ]  Create `/var/www/private/<environment>`
- [ ]  [ ]  Create private `run/`
- [ ]  [ ]  Create private `snomed/`
- [ ]  [ ]  Generate `config.php`
- [ ]  [ ]  Protect `config.php`
- [ ]  [ ]  Create MariaDB/MySQL database
- [ ]  [ ]  Create dedicated database user
- [ ]  [ ]  Import `health_history.sql`
- [ ]  [ ]  Apply all release migrations
- [ ]  [ ]  Confirm `app_config`
- [ ]  [ ]  Confirm `api_token`
- [ ]  [ ]  Confirm `ingest_queue`
- [ ]  [ ]  Configure Admin settings
- [ ]  [ ]  Configure Gemini
- [ ]  [ ]  Install SNOMED CT RF2 Snapshot
- [ ]  [ ]  Run SNOMED Import
- [ ]  [ ]  Verify SNOMED pipeline completion
- [ ]  [ ]  Run Image/SNOMED Summary Build
- [ ]  [ ]  Verify image/SNOMED coverage
- [ ]  [ ]  Configure persistent `queue_worker.php`
- [ ]  [ ]  Confirm workers in Import Monitor
- [ ]  [ ]  Test one synthetic/de-identified patient
- [ ]  [ ]  Review SNOMED, anatomy, evidence and audit
- [ ]  [ ]  Test Patient Record API if required
- [ ]  [ ]  Configure backups
- [ ]  [ ]  **Delete `setup_delete_after_setup.php`**
- [ ]  [ ]  Confirm the setup page is no longer web accessible

---

# Installation Order Summary

The complete first-run sequence is:

```text
1. Obtain source access
        |
        v
2. Deploy application
        |
        v
3. Configure domain + HTTPS
        |
        v
4. Open setup_delete_after_setup.php
        |
        v
5. Pass server preflight
        |
        v
6. Create private directories
        |
        v
7. Generate config.php
        |
        v
8. Create database
        |
        v
9. Load base schema + migrations
        |
        v
10. Configure Admin settings + Gemini
        |
        v
11. Install SNOMED RF2
        |
        v
12. Run SNOMED Import
        |
        v
13. Build Image/SNOMED database
        |
        v
14. Start supervised queue workers
        |
        v
15. Import one test patient
        |
        v
16. Review complete patient history
        |
        v
17. Test API if required
        |
        v
18. Configure backups
        |
        v
19. DELETE setup_delete_after_setup.php
        |
        v
SYSTEM READY

```

---

# Why the Setup Assistant Must Be Deleted

`setup_delete_after_setup.php` exists only to help bootstrap a new installation.

It performs checks and displays server information that is useful to an administrator during deployment but should not remain exposed afterwards.

It can display information such as:

- installed PHP version
- PHP extensions
- detected CLI PHP path
- expected private filesystem paths
- installation sequence
- application administration locations

It also contains a browser-side `config.php` generator.

Although the generator does not submit the entered credentials to the server, there is no operational reason for the installer to remain publicly available once the server has been configured.

The filename is deliberately explicit:

```text
setup_delete_after_setup.php

```

**When setup is complete, delete it.**