How it works
How it is built
The three processes on a Maran server, the contract between them, and the rules that decide what the root daemon is allowed to do.
A Maran server runs exactly three processes. The split between them is the product's main design decision, and most of the rules below follow from it.
The three processes
| Process | Runs as | Holds |
|---|---|---|
maran-api | an unprivileged system user | every piece of business logic, and the only database connection |
maran-agent | root | nothing — it is stateless, with no database and no configuration of its own |
| PostgreSQL | its own user | the panel's data, on a unix socket with no TCP listener at all |
The panel never touches the system. The agent never decides anything. That is the whole shape: a process that knows what should happen has no privileges, and the process with privileges knows only how to do a fixed list of things.
The contract between them
They speak over a unix socket, and what travels is a named operation with typed, validated arguments. Three properties follow, and each is a rule rather than a habit:
- There is no operation that runs a caller-supplied program. Not a restricted one, not a whitelisted one — none. So the set of things the root process can be made to do is finite, written down, and reviewable.
- There is no shell string anywhere in the exchange. A domain is not escaped before it reaches a configuration file; it is validated as a domain — letters, digits, hyphens and dots — so there is no newline in it to end one directive and begin another of the caller's choosing.
- The agent checks who is asking. Peer credentials on the socket are read before the operation is considered, so the daemon knows which process is on the other end rather than trusting what it is told.
Writing a configuration file
Every privileged write follows the same order, and the order is the point:
- render into a temporary file;
fsync;- rename it into place atomically;
- have the service itself validate the result —
nginx -t, not a regular expression of ours; - reload;
- roll the file back if either the validation or the reload refuses.
One bad render cannot take a server's other sites down, because the server's own parser is what decides whether the render was good.
Isolation between accounts
- An account is a real Linux user, with its own home directory and filesystem permissions.
- Its PHP runs in its own php-fpm pool under its own uid, and file operations run as that user — never as root.
- Tenant tables are scoped by account in the database, through query filters an automated test walks the whole model to verify.
- A request for another account's resource answers 404, never 403, so an error never confirms that a row exists.
What is deliberately absent
No message broker, no sidecar, no second daemon. Background work runs on durable queues stored in PostgreSQL, and installing Maran adds nothing to the server that is not in the table above — with one exception an operator turns on themselves, the FTPS service.
Paid modules do not change this either: they are C# modules that drive operations already compiled into the open agent. The agent never loads external code, which is why a licence cannot introduce a new privileged capability.