Horvan Edu Coaching · Self-hosted edition
Run it on your own server
The same app we host, installed on PHP hosting your centre controls: shared hosting such as Hostinger or cPanel, or your own VPS. Your data stays in your own database. Horvans gives you a licence for your domain and new versions as release packages.
What you get
Cloud (we host)
- Runs on Horvans at
/c/your-centre/ - ₹20 per active student per month, after a ₹10 trial
- Updates arrive by themselves
- Pay and download invoices in Admin, then Billing
Self-hosted (your server)
- Runs on your domain, with your database
- A licence for a set number of students and teachers, for a set period
- You install new versions from Horvans
- You look after hosting, backups and HTTPS
Price. There is no fixed public price for a self-hosted licence yet. It depends on the number of students and teachers and the length of the licence. Ask us or call +91 99437 08347 for a quote.
How a licence is issued. Horvans prepares a licence for your centre and signs it offline with the Horvans release key. It names your centre, the exact domain you will use (for example app.yourcentre.in), the number of students (seats) and teachers, the start and end dates and the modules. With the licence Horvans sends you:
- the release package
horvarn-edu-selfhost-<version>.zipand its checksum file.zip.sha256 - your licence file (a small JSON file)
- your sign-in client ID (it looks like
hz-sh-yourcentre), so the owner can sign in with Google
Sign-in still goes through the Horvans account service (Google). Students, teachers and parents sign in with the login and password your centre gives them; Google is only for the owner and admins.
Requirements
| What | Needed |
|---|---|
| PHP | 8.2 or newer, both for the website and on the command line (php -v). Extensions: openssl, pdo_mysql, mbstring, curl. |
| Database | MariaDB 10.6+ or MySQL 8. Two databases with their own users. They must be separate; one database for both is not supported. |
| Web server | Apache or LiteSpeed reading .htaccess files (with mod_rewrite). Most shared hosting works this way. On nginx you have to port the rules of the two .htaccess files yourself; this guide does not cover nginx. |
| Domain and HTTPS | A domain or subdomain used only for the app, such as app.yourcentre.in, with a valid HTTPS certificate (Let's Encrypt is fine). The app must sit at the top of that (sub)domain, not in a subfolder. |
| Command line | SSH or a terminal in your hosting panel. The installer runs only from the command line; there is no install-in-the-browser page. |
| Cron | A scheduled job every 5 minutes. |
| Outgoing HTTPS | The server must reach Horvans once a day for the licence check, and for Google sign-in. |
| E-mail (optional) | An SMTP mailbox for receipts and notices. Without it nothing is e-mailed; the app still works. |
Hostinger: Premium and Business web hosting include SSH, cron jobs, MySQL and free SSL. cPanel hosts: look for Terminal or SSH Access, Cron Jobs, MySQL Databases and SSL/TLS Status. If your plan has no SSH or terminal at all, talk to us before buying.
1. Get the release
- Ask for a licence (contact form or call). Tell us the centre's name, the exact domain you will use, how many students and teachers, and the Google e-mail of the owner.
- Horvans sends the zip, its
.sha256file, your licence file and your client ID. - Check the zip is complete and unchanged. The command prints a checksum; it must match the one in the
.sha256file.# Linux / macOS / VPS sha256sum horvarn-edu-selfhost-1.0.0.zip cat horvarn-edu-selfhost-1.0.0.zip.sha256 # Windows (Command Prompt) certutil -hashfile horvarn-edu-selfhost-1.0.0.zip SHA256
Inside the zip is one folder, horvarn-edu/, laid out like a website's document root:
horvarn-edu/
.htaccess web root rules (denies private files, routes /c/)
c/ the centre's address: /c/<your-centre>/
edu/api/lib/*-public.pem Horvans public keys (licence + revocation checks)
_hz_private/ private: denied to the web
platform/lib, migrations, bin, app
platform/config.selfhost.sample.php
platform/SELFHOST.md
MANIFEST.sha256 a checksum for every file
The package never contains passwords, private keys, test files or anyone's data.
2. Upload the files
Shared hosting (Hostinger hPanel or cPanel), with File Manager
- Create the subdomain (for example
app.yourcentre.in) and turn on SSL for it. Set its PHP version to 8.2 or newer. - Open File Manager in the subdomain's document root (often
public_htmlordomains/app.yourcentre.in/public_html). Make sure hidden files are shown, so you can see.htaccess. - Upload the zip there and choose Extract.
- Open the new
horvarn-edufolder, select everything inside it (including the hidden.htaccess) and move it up one level, into the document root. Then delete the emptyhorvarn-edufolder and the zip. - The document root now holds
.htaccess,c/,edu/,_hz_private/andMANIFEST.sha256.
With FTP (FileZilla or similar)
- Unzip the package on your computer.
- Turn on Show hidden files in your FTP program (in FileZilla: Server, then Force showing hidden files).
- Upload the contents of
horvarn-edu/into the subdomain's document root. Check that both.htaccessfiles arrived: the one in the root and the one inc/.
VPS
scp horvarn-edu-selfhost-1.0.0.zip licence.json you@server:/tmp/
ssh you@server
cd /tmp && unzip horvarn-edu-selfhost-1.0.0.zip
sudo rsync -a horvarn-edu/ /var/www/app.yourcentre.in/
sudo chown -R www-data:www-data /var/www/app.yourcentre.in/_hz_private
The web server's user must be able to write inside _hz_private/platform/: the app creates its private data/ folder there on first use.
Your licence file: keep it somewhere outside the web root (for example your home folder). The installer copies it into the private data/ folder.
3. Create the databases
Create two databases and two users, and give each user all privileges on its own database (the installer creates and later updates the tables).
Hostinger hPanel
Websites, then Databases, then Management: create a database with a new user, twice. Hostinger puts a prefix in front of the names (such as u123456789_); use the full names in the config file. The host is localhost or 127.0.0.1.
cPanel
MySQL Database Wizard: create the database, create its user, tick All privileges. Do it again for the second database.
VPS (MariaDB or MySQL)
CREATE DATABASE hz_platform CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE hz_edu CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'hz_platform'@'localhost' IDENTIFIED BY 'A-LONG-RANDOM-PASSWORD';
CREATE USER 'hz_edu'@'localhost' IDENTIFIED BY 'ANOTHER-LONG-RANDOM-PASSWORD';
GRANT ALL PRIVILEGES ON hz_platform.* TO 'hz_platform'@'localhost';
GRANT ALL PRIVILEGES ON hz_edu.* TO 'hz_edu'@'localhost';
FLUSH PRIVILEGES;
4. The config file
In _hz_private/platform/, copy config.selfhost.sample.php to config.php and fill it in. Every value below is a placeholder.
<?php
return [
'env' => 'prod',
'edition' => 'selfhost',
// your address: its host must be exactly one of the domains in your licence
'origins' => [
'www' => 'https://app.yourcentre.in',
'app' => 'https://app.yourcentre.in',
'api' => 'https://app.yourcentre.in',
'admin' => 'https://app.yourcentre.in',
'account' => 'ACCOUNT-ADDRESS-FROM-HORVANS', // the Horvans sign-in service
],
// two separate databases
'db' => [
'platform' => ['dsn' => 'mysql:host=127.0.0.1;dbname=hz_platform;charset=utf8mb4', 'user' => 'hz_platform', 'pass' => 'CHANGE_ME'],
'edu' => ['dsn' => 'mysql:host=127.0.0.1;dbname=hz_edu;charset=utf8mb4', 'user' => 'hz_edu', 'pass' => 'CHANGE_ME'],
],
'auto_migrate' => true, // cron applies the database changes a new version brings
'selfhost' => [
'licence_file' => '', // empty = _hz_private/platform/data/licence.json (the installer writes it)
'control_url' => 'CHECK-ADDRESS-FROM-HORVANS', // revocation list + daily check-in
],
// optional: your own mailbox for receipts and notices (empty = nothing is e-mailed)
'smtp' => ['host' => '', 'port' => 465, 'user' => '', 'pass' => '', 'from' => '', 'owner_alert_to' => ''],
// optional: a file with 64 hex characters that encrypts your own Cashfree secret (parents paying fees online)
'secrets' => ['gateway_key_file' => ''],
// from Horvans, with the licence
'tenancy' => ['client_id' => 'hz-sh-yourcentre'],
];
- origins: use the same
https://address four times. Do not mixwww.and nowww.; the licence check compares the host exactly. - account and control_url: Horvans confirms both addresses when it sends your licence. The sample file in the package may still show an older Horvans address; use the ones Horvans gives you.
- Passwords: only in this file, which the web never serves. After saving, make it readable by you and the web server only:
chmod 600 config.php(File Manager: Permissions 600). - env stays
prod. Inprodthe app refuses to run without database settings and never trusts the visitor's Host header.
5. Run the installer
Open SSH (or your panel's terminal), go to the document root and run, with your own values:
cd ~/domains/app.yourcentre.in/public_html # your document root
php -v # must say 8.2 or newer
php _hz_private/platform/bin/selfhost-install.php \
--licence=$HOME/licence.json \
--owner-email=owner@yourcentre.in \
--owner-name="Owner Name"
The installer:
- checks PHP and the extensions;
- creates (or updates) the tables in both databases;
- verifies the licence: the Horvans signature, the product, and that this server's address is one of the licensed domains;
- creates your centre and makes the owner e-mail its first admin.
It ends by printing the centre's address, the sign-in details to send to Horvans (client_id and redirect_uri, which is https://app.yourcentre.in/c/_auth/callback) and the cron line. It is safe to run again.
If php -v shows an older version than your website uses, your host has a separate PHP 8.2+ program for the command line. Your host's help pages give its path; use it in place of php here and in the cron job.
6. Set up cron
Add one job that runs every 5 minutes:
*/5 * * * * php /home/USER/domains/app.yourcentre.in/public_html/_hz_private/platform/bin/cron.php
- Hostinger: Advanced, then Cron Jobs, Custom, and paste the command part (everything after the five time fields).
- cPanel: Cron Jobs, Once Per Five Minutes, and the command.
- VPS:
crontab -eas the web server's user (for examplesudo crontab -u www-data -e).
Cron applies database updates after a new version, keeps the centre's status in step with the licence, sends queued e-mail and, once a day, checks the licence with Horvans. Without cron the daily check never happens: after 3 days the centre shows a warning and after the offline grace (14 days unless your licence says otherwise) it becomes read-only.
7. Activate the licence and sign in
- Send the installer's
client_idandredirect_urilines to Horvans. Google sign-in for your owner works once Horvans has registered them. - Check the licence from the command line:
php _hz_private/platform/bin/selfhost-install.php --status. It shows the state, licence ID, expiry, seats and the last check with Horvans.--checkruns the daily check now. - Open
https://app.yourcentre.in/c/yourcentre/. The owner chooses Sign in with Google with the e-mail given to the installer. - In the app, Admin, then Billing shows the licence: state, seats used, expiry and the last check. Add staff, batches, students and parents, and give each of them a login.
How the licence works
On every request (at most every 10 minutes) and from cron, the server checks the licence's signature against the Horvans public key in the package, the product, that its own address is a licensed domain, and the expiry. Once a day it downloads the Horvans revocation list (signed with a separate Horvans key; a list signed by anyone else is ignored) and checks in. The check-in sends only your centre's short name, the licence ID, the app version and your domain: no names, no student data.
| Licence state | When | Your centre |
|---|---|---|
| Valid | Signature, domain and dates are good; checked in within 3 days | Works normally |
| Grace | Expired less than the grace days ago, or the daily check is 3 or more days overdue | Still works; renew or reconnect |
| Expired, withdrawn, offline too long, not valid | After the grace days; the licence was withdrawn; or the file, domain or server clock is wrong | Read-only: staff can read and export everything, nothing new can be saved |
Seats work like the trial limits: once the licence's number of active students (or teachers) is reached, adding one more is refused until a student leaves or the licence is renewed with more seats.
Renewing or adding seats: Horvans signs a new licence for the same centre and domain. Install it with:
php _hz_private/platform/bin/selfhost-install.php --renew=$HOME/new-licence.json
Updates
Not automatic yet. For the self-hosted PHP edition there is no one-click or automatic updater today. A new version comes from Horvans as a new release package, which you install by hand as below. Your licence is signed by Horvans; the release package comes with SHA-256 checksums (the .zip.sha256 file and MANIFEST.sha256 for every file inside) rather than a signature of its own.
- Take a backup first (next section).
- Check the new zip's checksum against its
.sha256file. - Upload the contents of its
horvarn-edu/folder over the old files, exactly as in step 2. Yourconfig.phpand the privatedata/folder are not in the package, so they are not overwritten. - Within 5 minutes cron applies any database changes (because
auto_migrateis on). To apply them at once, runphp _hz_private/platform/bin/migrate.php, then--statusto check the licence. - Open the centre and check Admin, then Billing shows the new app version.
Files that a new version no longer uses are not removed by copying over the old ones. They are harmless; Horvans tells you in the release notes if one must be deleted.
Backups
Back up these, at least daily for the databases and after every change for the files:
- Both databases, with your host's backup tool or:
mysqldump --single-transaction -u hz_platform -p hz_platform > hz_platform-$(date +%F).sql mysqldump --single-transaction -u hz_edu -p hz_edu > hz_edu-$(date +%F).sql _hz_private/platform/config.php(it holds your database passwords: store the copy safely)._hz_private/platform/data/: your installed licence and the server's private keys.data/keys/gateway.keyis needed to read your saved Cashfree secret if parents pay fees online.
The app can also give you everything your centre has stored as one JSON file: Admin, then Billing, then Export centre data. That export is a copy for you to keep; restoring a server is done from the database backups.
To move to a new server: copy the files and those three items, import both databases, point the domain at the new server, then run --status.
Troubleshooting
Start with php _hz_private/platform/bin/selfhost-install.php --status, and look in your host's PHP error log.
| You see | What to do |
|---|---|
config.php must set 'edition' => 'selfhost' | config.php is missing, in the wrong folder (it belongs in _hz_private/platform/) or still says cloud. |
missing requirement: … | Turn on that PHP extension, or use the PHP 8.2+ command-line program (step 5). |
this server (…) is not a licensed domain | The host in origins.app must be exactly a domain in your licence (watch for www.). If the domain changed, ask Horvans for a new licence. |
release public key missing | edu/api/lib/release-public.pem did not upload. Upload the package again. |
bad signature or licence is for another product | The licence file was changed or is the wrong file. Use the file Horvans sent, unedited, or ask for it again. |
404 or "Not found" on /c/yourcentre/ | The .htaccess files are missing, or the server ignores them (mod_rewrite off, or nginx). |
| "This centre does not exist" on the bare domain | Open the full address /c/yourcentre/; the short name is the one in your licence. |
| Google sign-in fails or shows a redirect error | Horvans has not registered your client_id and redirect_uri yet, or tenancy.client_id in the config is wrong. |
| Centre is read-only | --status gives the reason: expired, withdrawn, offline too long, or "the server clock is behind" (fix the server's date and time). |
"could not reach Horvans" / last error after --check | The server cannot make outgoing HTTPS requests, or the control_url is wrong. |
| Blank page or error 500 | The website's PHP version is below 8.2, or a database setting is wrong. The PHP error log says which. |
| No e-mails | Fill in smtp in the config. Until then messages stay queued. |
Getting help
Call +91 99437 08347 or use the contact form. Send your licence ID and the output of --status. Never send your config.php, database passwords or anything from the data/ folder.
Want us to do it all instead? The cloud edition needs no server: start a ₹10 trial.