Solvo Solvo 2.0

Installation

This platform is plain PHP + MySQL/MariaDB — no Composer, no build step, no framework. It runs on shared cPanel hosting (the environment it was built for) just as well as a VPS, a dedicated server, or a local dev environment (XAMPP/MAMP/native LAMP). This guide covers all of those.

Requirements

You do not need: Composer, Node/npm, SSH access (though it helps), or any particular web server — Apache, LiteSpeed, and Nginx are all covered below.

1. Get the files onto your server

Upload the entire project (unzip it if you downloaded it as a zip) to your server. On cPanel, this is usually via File Manager or FTP into a directory outside public_html (see Step 6 for why).

2. Create the database

Using phpMyAdmin, cPanel's "MySQL Databases" tool, or the mysql CLI, create an empty database and a database user with full privileges on it. Note the database name, username, password, and host (usually 127.0.0.1 or localhost — cPanel sometimes uses a different host for MySQL specifically; check cPanel's MySQL page if unsure).

Don't run db/01_schema.sql manually yet — the installer (Step 4) does this for you in one click. If you'd rather do it by hand (e.g. via phpMyAdmin's SQL tab, or mysql yourdb < db/01_schema.sql), that's fine too — the installer detects an already-applied schema and skips straight past that step.

3. Configure .env

Copy .env.example to .env (same directory as bootstrap.php, i.e. the project root) and fill in real values:

cp .env.example .env

At minimum, set: - DB_HOST, DB_NAME, DB_USER, DB_PASS — from Step 2 - INSTALL_ENABLED=true — temporarily, just for the next step

Everything else (M-Pesa, SMS, ElimuPay secrets, etc.) has safe placeholder defaults and can be configured later, once you're ready to actually use those features — the platform runs fine without them (M-Pesa/SMS features simply won't work until credentialed; see each MODULE_*_NOTES.md for what depends on what).

If your host doesn't support .env files (some very locked-down shared hosts don't allow files starting with a dot, or your control panel sets environment variables its own way instead): set the same variable names as real environment variables through your hosting control panel instead. EnvLoader only reads .env if the file exists — if it doesn't, getenv() calls throughout the app fall through to whatever's set at the server/control-panel level, with no code changes needed either way.

4. Run the installer

Visit https://your-domain.example.com/install.php in a browser (adjust the path if your document root isn't set yet — see Step 6 first if you're unsure where that URL should point).

The installer: 1. Checks PHP version, required extensions, and that storage/ is writable. 2. Checks the database connects. 3. Applies the schema (db/01_schema.sql) if it hasn't been already — one click. Tick Also load sample data if you want two fully populated demo schools to explore (every role signs in with Demo@2026); leave it unticked for a live installation. 4. Lets you create your Super Admin account — name, email, password. This is the one and only account that can onboard schools and manage the platform; nothing else in the system can create it, which is exactly why this installer exists.

When the account is created the installer lists the cron jobs to add (step 7), with commands already pointing at your folder — copy them from there.

Immediately after your Super Admin account is created: set INSTALL_ENABLED=false in .env (or delete public/install.php entirely — either works). The installer refuses to create a second Super Admin once one exists, but there's no reason to leave the door open at all once you're done with it.

If you ever need to run it again on a fresh database (a new staging environment, for example), the same three steps apply: set INSTALL_ENABLED=true, visit the URL, turn it back off when done.

5. Activate your licence

Enter the licence key from your purchase (it starts with MJB-) in the installer's last step, next to your Super Admin details — it's activated straight away and the result is shown. No key yet? Skip it: after you sign in, Solvo takes you to System & licence, where you paste it and press Activate. Nothing to edit in .env.

A licence is valid for one domain. Moving to a new domain? Contact support to have the key released first; activating an already-bound key on a second domain fails by design.

You can see the licence status at any time under Super Admin → System & licence, along with the database version, the email/SMS queue, server checks and the cron list. If the licence server can't be reached, Solvo keeps working for a grace period (three failed checks, 12 hours apart) before it asks for re-activation, so a short outage on our side never takes your schools offline.

(1.x installations that used SYNC_KEY in .env keep working — the key is picked up automatically.)

6. Point your web server at public/

This is the single most important configuration step. The public/ directory is the only one meant to be reachable by a browser — everything else (core/, config/, db/, storage/, vendor/) contains application code, credentials, or uploaded files that must never be served directly.

.htaccess files are already in place in every non-public directory as defense-in-depth (they deny all access outright, so even a misconfigured document root won't expose them on Apache/LiteSpeed) — but the correct setup is still to point your document root at public/ directly, not rely on that safety net alone.

cPanel

If this is an addon domain or subdomain, cPanel's "Domains" page lets you set the Document Root directly to .../mojamoza/public when you create it. If it's your primary domain and you can't easily change its document root, either: - Move the whole project up one level and point the domain at a folder that only contains what's in public/ today (least convenient), or - Ask your host to set the document root for you (many will), or - Use a subdomain for the app instead of the primary domain's root.

Apache / LiteSpeed (VPS or dedicated server)

<VirtualHost *:443>
    ServerName your-domain.example.com
    DocumentRoot /var/www/mojamoza/public

    <Directory /var/www/mojamoza/public>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Nginx

.htaccess files do nothing under Nginx — use this instead:

server {
    listen 443 ssl;
    server_name your-domain.example.com;
    root /var/www/mojamoza/public;
    index index.php;

    # Deny the non-public directories outright, mirroring what the
    # .htaccess files do on Apache.
    location ~ ^/(core|config|db|storage|vendor|scripts)/ {
        deny all;
        return 404;
    }
    location ~ /\.(env|git) {
        deny all;
        return 404;
    }

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;  # adjust to your PHP-FPM socket/version
    }
}

Local development (XAMPP/MAMP/php -S)

Quickest option — from the project root:

php -S localhost:8000 -t public

Then visit http://localhost:8000/install.php.

7. Set up cron jobs

Six background scripts run on a schedule. The most important one is process_outbox.php (every 5 minutes): it sends queued emails and SMS and retries failures. The others handle overdue invoices, fee reminders, attendance alerts and so on — see docs/CRON_JOBS.md for the full list and exact cron syntax for both cPanel and a standard crontab.

8. Configure optional integrations (whenever you're ready)

None of these block getting the platform running — configure them when you actually need the feature:

Post-install security checklist

Troubleshooting

"This site can't be reached" / blank page — check your PHP error log (cPanel: "Errors" under Metrics, or error_log in the same directory as the failing script). Almost always a PHP version mismatch or a missing extension — re-run install.php's requirements check to confirm.

Installer says "Could not connect to the database" — double-check DB_HOST; on some hosts this is a specific hostname (like localhost:3306 or a dedicated internal hostname) rather than 127.0.0.1. cPanel's MySQL Databases page usually states the correct host.

Login page loads but signing in does nothing / redirects back to login — check that session.secure_cookie in config.php matches reality: if you're testing over plain HTTP (e.g. local dev without HTTPS), the secure-cookie flag will prevent the session cookie from ever being set. For local development without HTTPS, temporarily set it to false in config.php — never do this in production.

"Installation already complete" but you don't have Super Admin credentials — either use /forgot-password.php, or if that's not viable (e.g. the email on file is no longer accessible), connect directly to the database and update the relevant row's email in the users table so a reset can be sent to an address you control, then use the reset flow. There is deliberately no UI path around the "one super admin creation" safeguard.

403 Forbidden on every URL, including ones that don't exist — your document root is pointing at the project root rather than public/. The project root's own .htaccess denies all access as a safety net for core/, config/, etc., and Apache cascades that deny into every subdirectory — including public/ — unless something explicitly overrides it there. public/.htaccess does exactly that (a Require all granted block), so this should work out of the box; if you're still seeing it, confirm you have the current version of that file, and that your host has AllowOverride All enabled (some very locked-down shared hosts disable .htaccess processing entirely). Either way, the properly supported fix is still to point your document root directly at public/ — see step 6.

The platform is briefly unreachable, or won't activate — sign in as Super Admin and visit Super Admin → System & licence to see the actual activation status. Confirm the key was pasted exactly as given, and that you're checking the same domain the license was activated on — a license only works on the one domain it was first activated for. If the status page shows "Active" but something still feels wrong, the issue is unrelated to licensing; if it doesn't, contact support with your license key.

Updating an existing installation

Upload the new files over the old ones (keep your .env, config/edition.php and storage/), then sign in as Super Admin and open System & licence → Database. If an update is waiting, press Update the database now — Solvo applies only the changes it hasn't applied yet and records each one, so it is safe to press more than once. With SSH you can run php scripts/migrate.php instead.

Coming from 1.x? Follow UPGRADING_FROM_1X.md.