Production-grade HTTP/HTTPS web server with automated ACME SSL, PHP-FPM process isolation, and dynamic .htaccess parsing.
Web Server
SSL / HTTPS
PHP-FPM
.htaccess Parser
onifast-aero is the production-grade HTTP/HTTPS web server for the Mulvox hosting stack.
It reads virtual-host configurations from JSON files, serving multiple domains on a single IP with per-domain PHP-FPM socket proxying, automatic SSL via ACME, IP blocking via the fail2ban database, and native .htaccess directive parsing.
Core Capabilities
Dynamic Virtual Hosts
Lookup virtual domains from JSON configurations dynamically on every HTTP request.
Let's Encrypt / ZeroSSL
Automated provisioning and key handshakes using the standard ACME HTTP-01 challenges.
.htaccess Directive Engine
Native Go parser reading Apache-compatible rewrite conditions, headers, and reverse proxies.
Port Allocations
| Port |
Protocol |
Description |
| 80 |
HTTP |
Plain HTTP traffic for all hosted domains (redirectable) |
| 443 |
HTTPS |
TLS-terminated HTTPS traffic for all hosted domains |
| 4030 |
Internal HTTP |
Internal API challenge listener (SSL generation) — never expose! |
Virtual Hosting Architecture
Every domain registered inside the panel populates a dedicated JSON file in the config directory. When an HTTP header arrives, onifast-aero reads the Host header to resolve the config from /home/onifast-system/config/domains/<domain_name>.json:
- Per-domain Document Roots — Scoped as
/home/<user>/public_html/<domain>.
- Per-domain PHP-FPM Sockets — Isolated via UNIX sockets at
/run/php/php8.2-fpm-<user>.sock.
- Local .htaccess Configurations — Parsed hierarchically on every incoming client thread.
- TLS Certificates — Automated binding using certificates resolved by the ACME framework.
Domain JSON Configuration Layout
The files in /home/onifast-system/config/domains/ store structured JSON representing the site parameters. Here are typical configuration structures based on the service_type:
1. Standard Website (PHP/Static)
{
"domain": "example.com",
"owner": "alice",
"app_path": "/home/alice/public_html/example.com",
"php_version": "8.2",
"ssl_status": true,
"created_at": "2026-06-14T12:00:00Z",
"is_subdomain": false,
"parent_domain": "",
"service_type": "php"
}
2. Reverse Proxy
{
"domain": "app.example.com",
"owner": "alice",
"app_path": "",
"php_version": "",
"ssl_status": true,
"created_at": "2026-06-14T12:00:00Z",
"is_subdomain": true,
"parent_domain": "example.com",
"service_type": "proxy",
"tunnel_target": "127.0.0.1",
"tunnel_port": 3000
}
3. Gateway Tunnel Target
{
"domain": "agent.example.com",
"owner": "alice",
"app_path": "",
"php_version": "",
"ssl_status": true,
"created_at": "2026-06-14T12:00:00Z",
"is_subdomain": true,
"parent_domain": "example.com",
"service_type": "tunnel",
"tunnel_target": "my-agent-subdomain",
"tunnel_port": 80
}
Domain-Specific Custom Listen Ports
Onifast Aero supports binding specific domains to dedicated custom HTTP ports (instead of or in addition to the standard ports 80/443). By setting the listen_port property inside a domain's JSON configuration, Aero dynamically instantiates a standalone TCP port listener to handle requests targeting that port. Request routing is still virtual-host-aware, ensuring only the mapped domain is served on the specified port.
Custom Port Configuration Example
To configure custom.example.com to run exclusively on port 8080:
{
"domain": "custom.example.com",
"owner": "alice",
"app_path": "/home/alice/public_html/custom.example.com",
"php_version": "8.2",
"ssl_status": false,
"created_at": "2026-06-14T12:00:00Z",
"is_subdomain": true,
"parent_domain": "example.com",
"service_type": "php",
"listen_port": 8080
}
ACME SSL Certificates
Certificates are auto-provisioned using HTTP-01 challenges. Port 4030 serves challenge files internally, and certificates are outputted per-domain inside this storage directory:
/home/root/mulvox/certs/example.com/fullchain.pem
/home/root/mulvox/certs/example.com/privkey.pem
The panel's mod_acme_internal.go module requests certifications by contacting the internal API listener on port 4030, handling key challenge token placements dynamically.
PHP-FPM Process Isolation
onifast-aero forwards matching .php requests to target pools over FastCGI. Each hosting account binds to a separate PHP-FPM process pool, offering CPU, memory, and permissions separation between active accounts.
PHP Sockets
Socket directories are configurable globally by setting the php_socket_dir key inside the serverconfig.json config file.
Native .htaccess Rewrite Parser
The server parses local .htaccess files inside document roots using a native Go interpreter (handler_htaccess.go). Directives are parsed on each request, ensuring instant configuration updates.
Supported Directives
| Directive |
Example Directive |
Description |
DirectoryIndex |
DirectoryIndex index.php home.html |
Define default index file searches for directory folders |
RewriteBase |
RewriteBase /app/ |
Map relative URL base paths |
ProxyPass |
ProxyPass /api http://localhost:8080/ |
Reverse proxy proxying with full WebSocket upgrades support |
RewriteCond |
RewriteCond %{HTTP_HOST} ^www\. |
Condition variables (supports logical AND/OR) |
RewriteRule |
RewriteRule ^old/(.*)$ /new/$1 [L,R=301] |
Regex URL rewriting and client redirects |
Redirect |
Redirect 301 /old /new |
Simple status-based redirect routing |
Header |
Header set X-Frame-Options "DENY" |
Add or clear HTTP headers on outbox payloads |
<If> |
<If "%{REQUEST_URI} =~ m#^/terms#"> |
Apply directives conditionally based on the requested URI (supports regex and exact match) |
Options |
Options -Indexes |
Enables or disables directory listings |
Require |
Require all denied |
IP/path access control rules |
SetEnvIf |
SetEnvIf User-Agent "curl" bad_bot |
Variables environment flags (e.g. block bot user agents) |
Common Rewrite Examples
WordPress Permalinks
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
Redirect HTTP to HTTPS
RewriteCond %{HTTP_HOST} ^yourdomain\.com$
RewriteRule ^(.*)$ https://yourdomain.com/$1 [L,R=301]
Reverse Proxy to Node.js
ProxyPass /app http://localhost:3000/
Allowing Iframe Embeds for Specific Paths
To allow specific paths to be embedded in iframes from other domains (e.g. legal agreements or EULA modals), wrap the header overrides inside an <If> block matching the requested URI:
<If "%{REQUEST_URI} =~ m#^/terms#">
Header always unset X-Frame-Options
Header always set Content-Security-Policy "frame-ancestors *"
</If>
Block .env Files
<FilesMatch "^\.env$">
Require all denied
</FilesMatch>
Server-Sent Events (SSE) Broker Hub
onifast-aero features a built-in Pub/Sub Event Broker that allows highly concurrent, real-time messaging without blocking backend processes (such as PHP-FPM).
How It Works
- Auto Cookie Generation: Aero automatically sets a unique
ONIFAST_SSE_ID cookie on first browser contact and forwards it to backends via the X-SSE-ID HTTP header.
- Retrieve Current SSE ID in PHP: You can read the current browser's SSE ID in PHP using
$_SERVER['HTTP_X_SSE_ID'].
- Client Connection: Browsers connect to
/mulvox-sse/subscribe?channel=<name>. Go holds the connection open efficiently.
- Backend Publishing: Backend apps (PHP, NodeJS, Python) publish events using a quick, local HTTP POST request to
http://127.0.0.1/api/internal/sse/publish.
1. Browser Subscription (JavaScript)
// Connect to channel 'chat-room'
const eventSource = new EventSource('/mulvox-sse/subscribe?channel=chat-room');
// Listen to customized events
eventSource.addEventListener('new_message', function(event) {
const data = JSON.parse(event.data);
console.log("New message: ", data);
});
2. Backend Trigger (PHP Example)
Trigger events from PHP without spawning infinite loops or blocking PHP-FPM worker pools:
<?php
$payload = [
'channel' => 'chat-room',
'event' => 'new_message',
'data' => json_encode(['text' => 'Hello from PHP!'])
];
$ch = curl_init('http://127.0.0.1/api/internal/sse/publish');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
?>
3. Target Specific Client (Private Messaging)
Every browser is automatically subscribed to its own X-SSE-ID. To send a private message to a specific connection, target that ID as the channel:
<?php
$targetSseId = 'sse_1_18e12fa6b30'; // The target SSE_ID from HTTP_X_SSE_ID
$payload = [
'channel' => $targetSseId, // Target the ID as the channel name
'event' => 'private_message',
'data' => json_encode(['text' => 'Direct message!'])
];
$ch = curl_init('http://127.0.0.1/api/internal/sse/publish');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
?>
Systemd Service Configuration
[Unit]
Description=Mulvox Web Server
After=network.target
[Service]
ExecStart=/home/root/go/onifast-aero
WorkingDirectory=/home/root/go
Restart=always
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
Kernel Capabilities
The AmbientCapabilities=CAP_NET_BIND_SERVICE directive is required. It allows the Go binary to bind securely to standard public ports 80 and 443 without needing root privilege access.