# Installing Quaeriva

Quaeriva runs on ordinary shared hosting. There is **no Composer, no npm, no build step and no command line required** — you upload files and complete a form in your browser.

---

## 1. What you need

| Requirement | Minimum |
|---|---|
| PHP | 8.1 or newer |
| PHP extensions | Required: `pdo`, `pdo_mysql`, `json`, `session`; `mbstring` recommended |
| Database | MySQL 5.7+ or MariaDB 10.3+ |
| Web server | Apache or LiteSpeed with `mod_rewrite`, or nginx. A fallback exists if rewriting is unavailable — §6 |

The installer checks all of this before it lets you continue. Missing required support blocks installation; missing `mbstring` is shown as an advisory because Quaeriva includes UTF-8 fallbacks. You do not need to verify it yourself.

**You do not need:** SSH, Composer, Node, Redis, a cron job, or a background worker.

---

## 2. Create the database

In cPanel (or your host's panel):

1. Create a database.
2. Create a database user and give it **all privileges** on that database.
3. Note the database name, username and password — the installer asks for them.

Set the collation to **`utf8mb4_unicode_ci`** if your panel offers a choice. This is what stores Arabic, emoji and every other script correctly. If there is no choice, Quaeriva sets it itself.

**Leave the database empty.** Do not import any SQL file. The installer creates every table.

> Never use the MySQL `root` account, and never a blank password, on a live site.

---

## 3. Upload the files

Extract the release archive and upload its contents by FTP or your host's file manager.

Where you put them decides which of two layouts you are using.

### Layout A — document root points at `public/` (recommended)

If your host lets you change the document root, upload the whole project **above** the web root and point the document root at `public/`:

```
/home/you/quaeriva/          ← the project, not web-accessible
/home/you/quaeriva/public/   ← document root
```

This is the safest arrangement: application code, configuration and the database credentials are physically outside anything the web server will serve.

**How to point the document root at `public/`** — three ways, in order of preference:

*1. Your host's control panel.* In cPanel this is **Domains** → the domain →
edit **Document Root**. On a Plesk or DirectAdmin panel the field has the same
name. This is the cleanest route and needs no shell.

*2. Over SSH, by replacing `public_html` with a link.* Only when the panel has
no such field:

```bash
cd ~
mv public_html public_html.backup      # keep it until the site works
ln -s quaeriva/public public_html
ls -l public_html                       # should show -> quaeriva/public
```

If Apache refuses to follow the link ("403 Forbidden"), the server has
`FollowSymLinks` disabled and this route is closed — use option 3.

*3. If neither is possible*, do not fight it: use **layout B** below. It is
supported, and the shipped root `.htaccess` is what makes it safe.

> Do not simply move the *contents* of `public/` up one level. The application
> expects `public/` to exist and resolves its own paths from the project root.

### Layout B — everything in `public_html` (most common)

If you cannot move the document root — the usual case on cheap shared hosting — upload everything into `public_html`:

```
/home/you/public_html/        ← document root, and the project root
```

This works, and the shipped `.htaccess` in the project root is what makes it safe: it denies access to `.env`, `*.sql`, `*.md`, the archives, and the `app/`, `config/`, `database/`, `docs/`, `tests/` and `bin/` directories, then routes everything else into `public/index.php`.

**In this layout, `.htaccess` is a security control, not an optimisation.** After installing, verify it is working — see §5.

### Subdirectory

To serve the site from `example.com/community/` rather than the domain root, just upload it there. **The installer detects the folder and pre-fills it** as the *Site path* field, and writes it to `.env` for you:

```
APP_BASE_URL=/community
```

Every page, asset, API, canonical and redirect URL then uses that base path. Do not hard-code the folder name anywhere else. If the detected value is wrong, correct it in that field before submitting — or edit `.env` afterwards.

---

## 4. Run the installer

1. Open `https://your-domain/install` in a browser.
2. The first screen lists the requirement checks. Anything marked ✗ must be fixed before continuing.
3. Enter the database host, port, name, username and password.
   - On shared hosting the host is almost always **`localhost`**, not an IP address, and the port is **3306**.
   - The database name and username are usually **prefixed by your account name** — `myaccount_quaeriva`, not `quaeriva`. Copy them exactly as your panel shows them.
4. Enter your **site name** and a one-line **description**. Both appear in page titles, search results and link previews, and both can be changed later in Administration → Settings.
5. Enter the **Public site URL** as the trusted HTTPS origin only (`https://example.com`, without a folder). Password-reset and verification emails use this value instead of trusting the incoming request host.
6. Check the **Site path**. It is pre-filled from the folder you uploaded to (`/ask`, or empty for the domain root) and only needs changing if it is wrong.
7. Enter the administrator name, email and a password of **at least 12 characters**.
8. Choose the interface language. The installer opens in English; append `?lang=ar` for Arabic.
9. Submit.

The installer creates every table, creates your administrator account, writes the database credentials to `config/database.local.php`, writes the site name and description to the database, writes `APP_URL` and the site path to `.env`, and writes an installation lock so the wizard cannot be run a second time.

If something fails part-way, the installer rolls back rather than leaving a half-created site, and tells you what went wrong. The message names the actual cause — a refused password, a database that does not exist, an unreachable host — so act on what it says rather than guessing.

### After it finishes

- Sign in at `/login`, then open `/admin`.
- If your host allows it, make `config/database.local.php` read-only.
- Keep the installation lock file. Deleting it re-opens the installer.

---

## 4b. Move the admin panel (optional, recommended)

The panel answers on `/admin`, which is the first path any scanner tries. To move it, add this to `.env` in the project root:

```
APP_ADMIN_PATH=studio
```

The panel is then at `/studio`, `/admin` returns 404, and the link in the site header, the redirects and `robots.txt` all follow automatically. Letters, digits, `-` and `_` only; an invalid value falls back to `admin` rather than locking you out.

This is obscurity, not access control — the password and the role check are what actually protect the panel. It removes you from untargeted scanning, nothing more.

---

## 4c. Environment variables

Everything below is optional and lives in a `.env` file in the project root. `.env.example` is a commented copy — the installer writes what it needs, so most sites never create one.

| Variable | Default | What it does |
|---|---|---|
| `APP_URL` | required at installation | Trusted public HTTPS origin used for password-reset and verification links. No path; use `APP_BASE_URL` for folders |
| `APP_BASE_URL` | empty | The URL folder the site is served from. The installer detects and writes this. `/ask` for `example.com/ask`, empty for the domain root, `/index.php` for hosts that cannot rewrite URLs (§6) |
| `APP_ADMIN_PATH` | `admin` | The URL segment the admin panel answers on — see §4b |
| `APP_LOCALE` | `en` | Interface language before any site setting applies: `ar` or `en` |
| `DB_HOST` `DB_PORT` `DB_DATABASE` `DB_USERNAME` `DB_PASSWORD` | — | Only used when `config/database.local.php` is absent. That file takes precedence and is what the installer writes |

`.env` is denied by the shipped `.htaccess`. Verify that yourself with §5 — on a host that ignores `.htaccess`, a readable `.env` hands over your database.

---

## 5. Verify the installation (layout B only, but do it anyway)

Open each of these in a browser. **Every one must be refused** — a 403 or 404, never the file's contents:

```
https://your-domain/.env
https://your-domain/config/database.local.php
https://your-domain/database/migrations/001_create_users_categories_tags.sql
https://your-domain/tests/Public/task51.php
```

If any of them returns content, `.htaccess` is not being read by your server. Stop, and either enable `AllowOverride All` for the directory or move to layout A. Do not run the site publicly until these are refused.

---

## 6. If your host cannot rewrite URLs

Quaeriva's pretty URLs (`/q/my-question`) need the web server to send unknown
paths to `index.php`. On Apache and LiteSpeed that is `mod_rewrite`, driven by
the shipped `.htaccess`. Nearly every shared host has it, and if yours does not,
asking support to enable `mod_rewrite` is usually a one-line request — try that
first.

If it genuinely cannot be enabled, Quaeriva still runs. Create a `.env` file in
the project root containing:

```
APP_BASE_URL=/index.php
```

Every generated URL then routes through the front controller explicitly:

```
https://example.com/index.php
https://example.com/index.php/q/my-question
https://example.com/index.php/admin?page=overview
```

No rewriting is involved, so this works on any server that runs PHP. The links
are less tidy, and they are what search engines will index — so use this only
when rewriting is truly unavailable, and remove the setting if it becomes
available later.

**Symptom that you need this:** the home page loads but every other page returns
the web server's own 404, and CSS and images are missing.

## 7. nginx

nginx does not read `.htaccess` at all, so **layout A is required** — point the document root at `public/` and add:

```nginx
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

location ~ /\. {
    deny all;
}
```

Do not use layout B on nginx.

---

## 8. First steps after installing

| Do this | Where |
|---|---|
| Set the site name and description | Administration → Settings → General |
| Configure email (see below) | Administration → Settings → Mail |
| Choose colours, fonts, logo | Administration → Appearance |
| Create your categories | Administration → Categories |
| Add or remove languages | Administration → Languages |

### Email matters more than it looks

On shared hosting PHP's built-in `mail()` is frequently disabled or delivered straight to spam. If password-reset email does not arrive, **you can be locked out of your own site**.

Go to Administration → Settings → Mail, choose the SMTP transport, enter your provider's details, and use the **Send test email** button. Do this before you need it.

---

## 9. Common problems

| Symptom | Cause |
|---|---|
| **HTTP 500 on every page after installing** | The site cannot reach its database. Check `config/database.local.php` against your panel, then look in your host's PHP error log for the line beginning `Quaeriva: settings unavailable` — it names the driver's own reason (wrong password, unknown database, unreachable host) |
| **Home page returns 403 Forbidden, deeper URLs work** | An old `.htaccess`. The project root is a directory, so the front-controller rule skipped it, Apache found no `index.php` there and `Options -Indexes` answered 403. Fixed in 0.7.1-rc9 — replace the root `.htaccess` from the release archive |
| Every page except the home page is 404 | `mod_rewrite` is off, or `AllowOverride` forbids `.htaccess`. Ask your host to enable both; if they cannot, see §6 |
| 403 Forbidden after symlinking `public_html` | The server has `FollowSymLinks` disabled. Restore `public_html.backup` and use layout B |
| CSS and images 404 | Layout B without a working `.htaccess`; see §5 |
| "Requirement check failed" | A PHP extension is missing. The message names it; your host's PHP selector can usually enable it |
| Installer says it is already locked | The lock file exists. If this is genuinely a fresh install, remove `.quaeriva-installed.lock` from the project root |
| Arabic text shows as `????` | The database is not `utf8mb4`. Recreate it with the right collation and reinstall |
| Password-reset email never arrives | See §8 — configure SMTP |
| "The database refused the username or password" | Wrong user or password, or the user was never granted privileges on that database |
| "The database does not exist" | Create it in your panel first. Remember the account prefix — `myaccount_quaeriva` |
| "Could not reach the database server" | Use `localhost` rather than an IP address, and port 3306 |
| Site installs but every link points at the wrong folder | The Site path was wrong. Fix `APP_BASE_URL` in `.env` |

---

## 10. Upgrading later

See `docs/UPGRADE.md`. In short: back up first, replace the files, then use the **Upgrade database** action in the admin panel. Never re-import a demo database over a live one, and never edit a migration that has already run.

---

Requirements: PHP 8.2+, PDO MySQL, MySQL/MariaDB, OpenSSL; cURL is recommended for Telegram and Turnstile. Upload the release, copy `.env.example` to `.env`, set database and `APP_URL`/`APP_BASE_URL`, open `/install/`, then run `php bin/doctor.php`. Keep `APP_DEBUG=false` in production.
