# Key Lair — Installation Guide

This guide walks through installing Key Lair on a fresh **Ubuntu 24.04** server
with **Apache2** and **MySQL**, step by step, with the exact commands to run.
No prior Linux experience is assumed — every command is explained.

You'll need:
- SSH access to your server (an IP address, a username, and either a password or an SSH key)
- A domain or subdomain pointed at the server (e.g. `keys.yourdomain.com`) — optional but strongly recommended for HTTPS
- About 30–45 minutes

---

## 1. Connect to your server

From a terminal (macOS/Linux) or PowerShell/PuTTY (Windows):

```bash
ssh your_username@your_server_ip
```

Enter your password (or it'll use your SSH key automatically) when prompted. You should
land at a command prompt on the server.

---

## 2. Install the required software

Update the package list and install PHP, MySQL, Apache, and a few PHP extensions Key
Vault needs:

```bash
sudo apt update
sudo apt install -y apache2 mysql-server php php-mysql php-mbstring php-gd php-zip unzip
```

- `apache2` — the web server
- `mysql-server` — the database
- `php`, `php-mysql` — runs the application and talks to the database
- `php-mbstring`, `php-gd` — used for text handling and image validation on uploads
- `php-zip` — needed for the in-app **Updates** page to unpack a downloaded update
- `unzip` — to extract the Key Lair package

When prompted `[Y/n]`, type `Y` and press Enter.

Check PHP installed correctly:

```bash
php -v
```

You should see something like `PHP 8.1` or newer.

Key Lair also sends email (password reset links) using PHP's built-in `mail()`
function, which needs a mail transfer agent installed on the server to actually
deliver anything. **This step is optional** — skip it if you don't want to bother
with mail right now, and turn off "forgot password" under **Mail** in the admin
panel (Settings → Mail) instead, so admins aren't offered a reset link that would
never arrive. It's on by default. You can also do this step later — from the same
**Mail** page, which shows whether a mail server is currently detected and lets you
send a test email — or during the setup wizard, which has an optional "Outgoing
mail" step covering the same thing.

If this server doesn't already send mail for something else, install and configure
Postfix in send-only mode:

```bash
echo "postfix postfix/main_mailer_type string 'Internet Site'" | sudo debconf-set-selections
echo "postfix postfix/mailname string $(hostname -f)" | sudo debconf-set-selections
sudo apt install -y postfix
```

This answers Postfix's setup prompts automatically instead of showing an
interactive dialog. No further configuration is required for outgoing mail to
work. Note that mail sent this way can still land in spam without proper DNS
records (SPF/PTR) for this server — fine for low-volume transactional email
like password resets, but not something to rely on for anything at scale.

---

## 3. Secure MySQL and create a database user

Run MySQL's built-in security setup:

```bash
sudo mysql_secure_installation
```

Answer the prompts — it's fine to say `Y` (yes) to everything unless you have a specific
reason not to. You'll be asked to set a root password for MySQL; **write it down somewhere safe**.

Now log into MySQL to create a dedicated user for Key Lair (never use the MySQL root
account for the application itself). The setup wizard in step 7 will create the actual
database for you, so you only need to create the *user* here:

```bash
sudo mysql -u root -p
```

Enter the MySQL root password you just set. You're now inside the MySQL prompt (it looks
like `mysql>`). Run these commands one at a time, replacing `CHOOSE_A_STRONG_PASSWORD`
with a real password you generate and save:

```sql
CREATE USER 'key_lair_admin'@'localhost' IDENTIFIED BY 'CHOOSE_A_STRONG_PASSWORD';
GRANT ALL PRIVILEGES ON key_lair.* TO 'key_lair_admin'@'localhost';
FLUSH PRIVILEGES;
EXIT;
```

(`GRANT ... ON key_lair.*` works even before the `key_lair` database exists — MySQL
grants are stored ahead of time, and the wizard will create the database itself.)

You're back at the normal command prompt now.

---

## 4. Upload the Key Lair files

From **your own computer** (not the server), upload the `key-lair.zip` file you
downloaded. Open a new terminal window on your computer (keep the SSH session open in
the other one) and run:

```bash
scp key-lair.zip your_username@your_server_ip:/home/your_username/
```

This copies the ZIP into your home directory on the server. Switch back to your SSH
session and unzip it:

```bash
cd /home/your_username
unzip key-lair.zip
```

This creates a `key-lair/` folder. Now move it into place under Apache's web root:

```bash
sudo mv key-lair /var/www/key-lair
sudo chown -R www-data:www-data /var/www/key-lair
```

The second command makes Apache's user (`www-data`) the owner, so the app can write
uploaded images and the server can read everything it needs.

---

## 5. Set up the Apache site

Create a new site configuration:

```bash
sudo nano /etc/apache2/sites-available/key-lair.conf
```

`nano` is a simple text editor. Paste in the following, replacing `keys.yourdomain.com`
with your actual domain:

```apacheconf
<VirtualHost *:80>
    ServerName keys.yourdomain.com
    DocumentRoot /var/www/key-lair/public

    <Directory /var/www/key-lair/public>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/key-lair-error.log
    CustomLog ${APACHE_LOG_DIR}/key-lair-access.log combined
</VirtualHost>
```

Save and exit (`Ctrl+O`, `Enter`, `Ctrl+X`).

Enable the site and the URL-rewriting module it needs:

```bash
sudo a2ensite key-lair.conf
sudo a2enmod rewrite
sudo systemctl reload apache2
```

At this point, visiting `http://keys.yourdomain.com/setup.php` in a browser should show
a "Welcome" page — that confirms Apache is serving the app correctly. If it doesn't, see
[Troubleshooting](#troubleshooting) below before continuing.

---

## 6. Make the config folder briefly writable

The setup wizard (next step) will write `config/config.php` for you automatically, so
Apache's user needs temporary write access to the `config/` folder:

```bash
sudo chmod 775 /var/www/key-lair/config
```

(You'll lock this back down in step 8, right after setup finishes.)

---

## 7. Run the setup wizard

Visit, in a browser:

```
http://keys.yourdomain.com/setup.php
```

This works like a typical web app installer:

1. **Welcome** — click through to get started.
2. **Database connection** — enter:
   - **Database host**: `127.0.0.1`
   - **Port**: `3306`
   - **Database name**: `key_lair` (created automatically if it doesn't exist)
   - **Database username**: `key_lair_admin`
   - **Database password**: the strong password you chose in step 3

   The wizard tests the connection before continuing. If it fails, double-check the
   username/password from step 3.
3. **Set up the database** — click the button to create all the required tables.

You'll be redirected straight to the admin login, where you'll create your admin account
next. The wizard also fills in `app.base_url` and generates a random encryption key for
you automatically — no manual editing needed.

---

## 8. Lock the config folder back down

Now that `config/config.php` has been written, remove write access again:

```bash
sudo chmod 755 /var/www/key-lair/config
```

---

## 9. Enable HTTPS (strongly recommended)

Install Certbot, which gets you a free, auto-renewing HTTPS certificate:

```bash
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d keys.yourdomain.com
```

Follow the prompts (enter your email, agree to the terms). Certbot will automatically
update your Apache config and set up renewal. Choose the option to **redirect HTTP to
HTTPS** when asked.

Your site is now available at `https://keys.yourdomain.com`.

**Note:** if you enabled HTTPS after already running the setup wizard, edit
`config/config.php` once and change `base_url` from `http://` to `https://`.

---

## 10. Create your admin account

Visit:

```
https://keys.yourdomain.com/admin/login.php
```

Since no admin account exists yet, you'll see a **"Create the master admin account"**
form. Fill in a username, email, and a strong password (10+ characters). This becomes
the **master admin** account — the only role that can manage every other account,
including other admins, and reach Migrations/Updates/Mail — from **Users** in the
sidebar. See **Users** in the admin panel for what each of the five roles (master
admin, admin, content manager, reviewer, demo) can do.

Once created, you're logged in automatically.

**Optional but recommended:** go to **Settings → Two-factor authentication** and set up
an authenticator app (Google Authenticator, Microsoft Authenticator, Authy, etc.) for
extra login security.

---

## 11. Create your first campaign

In the sidebar, go to **Campaigns → New campaign**. Give it a name (e.g. "Launch Wave")
and a default claim limit (how many keys a creator can claim per product by default —
usually `1`).

---

## 12. Create your first product

Go to **Products → New product**. Fill in:

- **Name** — the game's title
- **Cover image** — a 16:9 image, shown as the hero on the claim page
- **Intro text** — shown above the platform buttons (e.g. a thank-you message)
- **Outro text** — shown below the key, e.g. redemption instructions
- **Screenshots** / **YouTube URL** — optional, shown in the media gallery

Set **Status** to **Active** so it becomes claimable, then save.

---

## 13. Import your keys

Go to **Keys → Import keys**. Choose your campaign, product, and platform, then paste
your list of keys — one per line — or upload a `.csv`/`.txt` file. Repeat once per
platform you have keys for. The default platforms are Steam, Xbox, PlayStation 4/5, and
Switch/Switch 2 — manage them (add new ones, remove ones you don't need) under
**Platforms** in the sidebar. You can export all currently unused keys as a CSV at any
time via **Keys → Export unused (CSV)**.

---

## 14. Create a claim link and test it

Go to **Claim Links → New claim link**. Choose the campaign, enter a creator tag (e.g.
`@SomeStreamer`), tick the product(s) this link should grant, and create it. You'll get
a link like:

```
https://keys.yourdomain.com/claim/8f4d2c91c7e4a1b3...
```

Open it in a private/incognito browser window to see exactly what the creator will see.
Pick a platform and confirm you receive a key — then check **Redemptions** in the admin
panel to see it logged.

Send the link to your creator. That's it — no account, login, or registration needed on
their end.

---

## Troubleshooting

**Visiting `/setup.php` shows an Apache error or default page instead of "Welcome"** —
the VirtualHost in step 5 isn't active yet. Check `sudo apache2ctl configtest` for
errors, confirm `sudo a2ensite key-lair.conf` ran, and that your domain's DNS actually
points at this server.

**"Couldn't connect to MySQL with those details" in the wizard** — double-check the
username/password you created in step 3, and that MySQL is running
(`sudo systemctl status mysql`).

**"Couldn't write config/config.php"** — the `config/` folder isn't writable yet; go
back to step 6.

**"Table ... doesn't exist" (500 error) after updating the code** — a required database
migration hasn't been applied yet. Sign in as a master admin and check **Migrations** in
the sidebar, or apply it manually — see "Updating later" below.

**Password reset emails never arrive** — check **Mail** in the admin panel first: it
shows whether a mail transfer agent is currently detected, and has a "send test
email" button. If the status shows nothing detected, confirm Postfix (or whatever
mail transfer agent you're using) is installed and running:
`sudo systemctl status postfix`. Check `sudo tail -f /var/log/mail.log` while
triggering a reset to see whether the message was accepted, deferred, or bounced.
Also check the recipient's spam folder — mail sent this way has no SPF/DKIM setup
by default, so some providers may flag it. If you don't want to deal with mail at
all, turn off "forgot password" on the same **Mail** page instead.

**"Connected to MySQL, but couldn't create/select the database ... Access denied"** —
the database name you typed into the wizard doesn't match the one you granted privileges
on in step 3. Either enter `key_lair` in the wizard's "Database name" field (matching
the `GRANT ... ON key_lair.*` from step 3), or, if you want a different database name,
grant privileges on that name instead:
`GRANT ALL PRIVILEGES ON your_db_name.* TO 'key_lair_admin'@'localhost';`.

**A blank white page anywhere in the app** — turn on error display temporarily by
setting `'debug' => true` in `config/config.php`, reload to see the actual error, then
set it back to `false` once fixed (never leave debug mode on in production).

**500 Internal Server Error** — check the Apache error log:

```bash
sudo tail -50 /var/log/apache2/key-lair-error.log
```

**Pretty URLs like `/claim/...` show a 404** — make sure `mod_rewrite` is enabled
(`sudo a2enmod rewrite && sudo systemctl reload apache2`) and that your VirtualHost has
`AllowOverride All` as shown in step 5.

**Can't upload images / cover images don't save** — the uploads folder needs to be
writable by Apache:

```bash
sudo chown -R www-data:www-data /var/www/key-lair/public/uploads
```

If a specific upload fails outright rather than saving, the error message now names
the reason (as of 0.3.13) — most commonly a single file exceeding PHP's own
`upload_max_filesize`, which `public/.htaccess` already raises to 16M (and
`post_max_size` to 48M, for multi-screenshot submissions) via `php_value`, so this
should only come up at all if your Apache setup can't honor per-directory PHP
overrides — e.g. `AllowOverride All` isn't actually active for this vhost (see step 5
above), or you're running PHP via `php-fpm`/`mod_fcgid` instead of `mod_php` (`php_value`
in `.htaccess` only works for `mod_php`; a `php-fpm` setup needs the equivalent set in
its pool's `php_admin_value` directives, or in `php.ini` directly, instead).

**"Key Lair is already set up" when visiting `/setup.php`** — this is expected once
installation is complete; it's a safety measure so the wizard can't be run a second time
by accident. To reinstall from scratch, delete `config/config.php` and re-run steps 6–7.

---

## Manual configuration (alternative to the wizard)

If you'd rather not use `/setup.php` — for example, scripting an unattended install —
you can do the same two things it does, by hand:

```bash
sudo cp /var/www/key-lair/config/config.sample.php /var/www/key-lair/config/config.php
sudo nano /var/www/key-lair/config/config.php
```

Fill in `db.database`, `db.username`, `db.password`, `app.base_url`,
`mail.from_address` (e.g. `no-reply@yourdomain.com`), and generate a
`security.encryption_key` with:

```bash
php -r "echo bin2hex(random_bytes(32));"
```

Then create the database and import the schema:

```bash
sudo mysql -u root -p -e "CREATE DATABASE key_lair CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mysql -u root -p key_lair < /var/www/key-lair/sql/schema.sql
```

Then continue from step 10 (create your admin account) above.

---

## Updating later

The easiest way is from the admin panel: sign in as a master admin and go to
**Updates** in the sidebar. It checks automatically against the vendor's update server —
nothing to configure. The page shows your current `VERSION` against the latest published
release and updates with one click. This only replaces application files; it never
touches `config/config.php`, `public/uploads/`, or the database. Requires the `php-zip`
extension (step 2 above).

Otherwise: back up your `config/config.php` and your database
(`mysqldump -u root -p key_lair > backup.sql`), then replace the application files
(everything except `config/config.php` and `public/uploads/`) with the new version.

Then apply any new database changes. The easiest way is from the admin panel: sign in
as a master admin and go to **Migrations** in the sidebar, which lists every file in
`sql/migrations/` you haven't applied yet and runs it with one click. If you'd rather do
it over SSH instead:

```bash
sudo mysql -u root -p key_lair < sql/migrations/001_admin_password_resets.sql
```

Existing installs also need a `mail` section added to `config/config.php` (see
`config/config.sample.php` for the format) to use the "forgot password" email
feature — a fresh install gets this automatically from the setup wizard.

When `gitlab_project` is set there, the Updates page's Connection form becomes
read-only and always uses the config.php values, exactly like `db` credentials — the
value only ever lives on disk in `config.php`, never in the database.
