Developer docs
Start
Build with AI
Themes
Apps
- App development
- App development best practices
- Hooks and filters
- The App API
- Sending email from apps
- Admin bar for apps and themes
- Public routes for apps
- Admin screens, routes and UI components
- Dashboard widgets
- Data tables
- AJAX actions
- Media and uploads for apps
- App permissions
- Content types, blocks and widgets
- Content types, taxonomies and custom fields
- Search
- When an app fails: fault isolation and recovery
API and AI agents
Security and contributing
Installing for development
How to run Basehim on your own computer, what the server needs in detail, and how to set up Apache, nginx and a real cron job. Site owners installing on a hosting account want How to install Basehim on your hosting instead.
Requirements in detail
| Minimum | Notes | |
|---|---|---|
| PHP | 8.1 | 8.2 or 8.3 recommended |
| PHP extensions | pdo_mysql, mbstring, json, openssl, fileinfo | gd (smaller image sizes) and curl (updates, the marketplace, apps that call other servers) are recommended: the installer flags them but installs without them |
| Database | MySQL 5.7+ or MariaDB 10.3+ | One empty database and a user with full rights on it |
| Web server | Apache or LiteSpeed with mod_rewrite | nginx works with the rules below |
| PHP command line | optional | Lets Basehim test-load an app in a separate process before it runs on your site (see When an app fails). Found automatically on most hosts; proc_open must not be disabled |
| Disk | about 30 MB | plus your uploads |
The installer checks all of this before it asks anything else, and says how to fix anything that is missing.
On your own machine
git clone https://github.com/basehim/basehim.git
cd basehim
php -S localhost:8000 server.php
Then open http://localhost:8000 and run the installer. Use 127.0.0.1 rather than localhost as the database host if PHP can't find the MySQL socket.
server.php sends requests to index.php and serves the asset folders, like .htaccess does on Apache. It only works under php -S.
Tip: PHP's built-in server handles one request at a time. That is fine for development. But an app that calls its own site over HTTP will wait on itself forever. Call core in-process through
$this->api()instead (see The App API).
Files and folders the installer writes
The site folder must be writable while the installer runs, because it creates .env there. storage/ and content/ must stay writable afterwards (775 is enough; never use 777).
After the install, .env holds the database details, APP_URL, two random secrets (APP_KEY and JWT_SECRET) and INSTALLED=true. Anything your host had already put in it, such as TRUSTED_PROXIES, is kept. Keep a copy somewhere safe, and never put it in a public repository. The settings you can add are in Configuration.
Choosing the table prefix
There is nothing to set up. Since 1.2.70 the installer gives every site its own random table prefix, such as k1y_, and checks that no table in the database already uses it. Every table, including the ones apps create, gets that prefix, so any number of sites can share one database without touching each other's tables. Going back and retrying the installer reuses the prefix it chose the first time.
To choose the prefix yourself, put DB_PREFIX=site2_ in .env before running the installer; a prefix already in .env always wins. Never change it afterwards: the site would look for tables that don't exist.
Subfolder installs
Basehim detects its base path on every request, so a subfolder needs no configuration. Themes and apps should build links with $base, link_to(), theme_asset() or $this->asset() so they keep working there.
Apache
The bundled .htaccess does three jobs:
- sends every request that isn't a real file to
index.php; - passes the
Authorizationheader through to PHP, which bearer tokens need on many cPanel servers; - denies direct access to
app/,config/,database/,storage/(exceptstorage/uploads),routes/,tests/,bootstrap.php,.envand other dotfiles.
Keep it. If API calls with a valid key return 401, the Authorization header is being stripped: make sure .htaccess is uploaded and mod_rewrite is on.
nginx
nginx ignores .htaccess, so you have to repeat its rules yourself. Basehim is developed and tested on Apache; this server block mirrors the bundled .htaccess. Adjust root and the PHP-FPM socket:
server {
listen 80;
server_name example.com;
root /var/www/basehim;
index index.php;
# Public assets are served as files.
location ^~ /storage/uploads/ { try_files $uri =404; }
location ^~ /admin/assets/ { try_files $uri =404; }
location ~ ^/content/themes/[^/]+/assets/ { try_files $uri =404; }
# Never serve these directly.
location ~ ^/(app|config|database|storage|routes|tests)(/|$) { deny all; }
location ~ ^/(bootstrap\.php|server\.php)$ { deny all; }
location ~ /\. { deny all; }
# Everything else goes to the front controller.
location / { try_files $uri /index.php?$query_string; }
location ~ \.php$ {
# Only index.php and install.php are entry points.
location ~ ^/(?!index\.php|install\.php) { return 404; }
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
The \.php$ rule refuses PHP files other than the two entry points. That includes app and theme files, which Apache would otherwise run directly.
Background tasks and cron
Apps can schedule work (hourly imports, daily clean-ups). By default due tasks run after a page has been sent to a visitor, so nobody waits for them. The catch: a site with no traffic runs nothing.
For tasks that must run on time, point a real cron job at the scheduler. The URL contains a secret token. Get it from the API as an administrator: with an API key that has the settings:read scope, or with a JWT.
curl -s https://example.com/api/v1/schedule \
-H "Authorization: Bearer basehim_your_key" | grep -o '"cron_url":"[^"]*"'
Then add a crontab line (cPanel: Cron Jobs):
* * * * * curl -s "https://example.com/api/v1/schedule/run?token=YOUR_TOKEN" > /dev/null
A wrong token gets a plain 404, so the endpoint stays hidden.
More troubleshooting
| Symptom | Likely cause and fix |
|---|---|
| Every page except the home page is 404 on nginx | Add the try_files rule above. |
| API calls with a valid key get 401 | The server strips Authorization. Keep the bundled .htaccess; on nginx pass HTTP_AUTHORIZATION. |
"Not installed yet" from /api or /mcp | .env is missing. Open /install.php. |
| White page or "Something went wrong" | Set APP_DEBUG=true in .env briefly, or read storage/logs/basehim-YYYY-MM-DD.log. |
Links or images point to http:// on an HTTPS site | Set APP_URL=https://… in .env, and TRUSTED_PROXIES behind a proxy (see Configuration). |
| Scheduled tasks never run | No traffic and no cron. Add the cron line above. |
Related: Configuration and the .env file, Updates, migrations and backups, When an app fails.
Leave a comment