HisarBlok · documentation
Installation
From an empty server to a running panel and a published site: what you need, how the web server is set up, and what the setup wizard asks.
For version 0.13.1.
What you need
- PHP 8.4 or newer with
dom(with its HTML5 parser),pdo_sqlite(SQLite 3.27+),mbstring,zliband Argon2id support inpassword_hash(). Optional:gdfor image resizing and WebP/AVIF,opensslfor mail over TLS,xmlreaderfor WordPress imports,sodiumfor checking the signature of an update package. - nginx or Apache, with PHP-FPM.
cron, for the nightly backup and the one-minute job.- Nothing else: no Composer step, no npm step and no database server.
- Optional: MySQL or MariaDB instead of SQLite (MariaDB 10.6+ / MySQL 8.0.13+, InnoDB, utf8mb4, and PHP's
pdo_mysql). SQLite is right for almost every site.
php tools/install.php --check prints the same requirement list the wizard shows. On Debian 13 the PHP 8.4 packages cover it:
sudo apt install nginx php-fpm php-sqlite3 php-mbstring php-xml php-gd cron
Two addresses, three directories
The panel and the site are two different things and usually live at two different addresses:
| Address | Served directory | Runs PHP? | |
|---|---|---|---|
| The panel | panel.example.com | …/hisarblok/public/ | yes, every request |
| The site | example.com | the site's own document root | only /go.php and /get.php, when a module needs them |
The third directory is never served: …/hisarblok/data/. It holds all content and every account's password hash, the configuration, backups and the rate-limit store.
The one deployment detail that matters: point the panel's document root at
public/, not at the repository. Serve the root anddata/content.sqlitebecomes a download link. The wizard checks this with a real HTTP request before it lets you continue.
The site's document root must be a separate directory the PHP user can write to. It cannot be HisarBlok's own directory and it cannot contain data/. One installation can publish several sites; each has its own document root.
The web server
nginx. The panel's root is …/hisarblok/public and every unknown address goes to index.php. The site serves plain files; only the two visitor endpoints reach PHP:
server {
server_name example.com;
root /var/www/example.com/public;
index index.html;
error_page 404 /404.html;
location / { try_files $uri $uri/ =404; }
location ~ ^/(go|get)\.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock;
}
location ~ \.php$ { return 404; }
gzip_static on;
}
Apache. For the panel, DocumentRoot …/hisarblok/public with FallbackResource /index.php; for the site, ErrorDocument 404 /404.html and a PHP handler for go.php / get.php only.
HestiaCP. Add two web domains for the same user: the panel and the site. The code goes one level above the panel domain's public_html, and its public/ directory becomes public_html, so data/ and core/ stay outside what is served. If the domain's PHP sets open_basedir, it must include the panel directory.
Plesk. The code goes inside the subscription, outside httpdocs; the panel subdomain's document root is set to hisarblok/public, and the site's document root is httpdocs.
Recommended headers. For the site: X-Content-Type-Options: nosniff, a Content Security Policy of script-src 'self'; object-src 'none'; base-uri 'none', and Content-Disposition: attachment for the downloads folder. Built pages carry no inline script, so the policy does not break them.
Behind a proxy. Behind Cloudflare or another proxy, list it in trusted_proxies in the configuration (single addresses or CIDR ranges), or the sign-in and form limits count the proxy instead of the visitor.
The setup wizard
With no configuration yet, the panel address opens the setup wizard instead of a sign-in page. Its first question is a setup code: on the first visit the code is written to a file with an unguessable name in the server's data/ directory.
sudo sh -c 'cat /srv/hisarblok/data/setup-token-*'
That proves whoever fills in the wizard can also read files on the server, so an uploaded-but-unconfigured installation does not belong to whoever finds it first. Then, in order:
- Server check: the requirements, and a real request proving
data/is not served. - Panel language: English or Turkish, changeable later.
- Administrator: username, optional email and password. The password is stored in the database as an Argon2id hash.
- First site: domain, name, document root, languages, default language, theme.
- Database: SQLite (nothing to set up) or MySQL/MariaDB; the connection, version, collation and every right needed are tested before anything is written.
- Email: an SMTP relay for password resets and notifications, or Skip.
- Summary: nothing is written before you press Install.
Install creates the database, the administrator and the configuration, builds the site once and deletes the code file. From then on every one of the wizard's addresses answers 404.
From the command line. tools/install.php does the same without questions. The password is read from standard input rather than passed as an argument, so it never appears in the process list or the shell history.
sudo -u www-data php tools/install.php --user=admin --site=example.com \
--root=/var/www/example.com/public --languages=en,tr --lang=en
Check it from outside
After installing, request data/content.sqlite on both the panel and the site address: the answer must not be 200. A 403, a 404 or a redirect to the sign-in page is right.
The two scheduled jobs
In the PHP user's crontab:
15 3 * * * php /srv/hisarblok/tools/backup.php --quiet
* * * * * php /srv/hisarblok/tools/tick.php --quiet
The first makes a backup every night. The second sends merged notifications and finishes the build queue: a page saved in the panel is written at once, while lists, feeds, the sitemap and full rebuilds follow in the background. Without it a large rebuild only moves on while someone has the panel open. The panel's site health card shows when the job last ran.
First content
The first build writes only the 404 page and shared files. Sign in and, under Pages → New page, publish a page with an empty address: that is the home page. The blog, forum, members, comments, forms, downloads, search and menu are modules, off on a new installation; switch them on per site under Modules.