Skip to content

Philosophy

Dead simple. Solid as a mountain.

This document is the foundation. Every design decision, every line of code, every contribution is measured against what is written here. If code contradicts this document, the code is wrong. If this document needs to change, that is a significant event requiring explicit agreement — not a casual edit.


Reality is always the source of truth.

Never store what can be read directly. Never assume what can be verified. Never represent what must remain honest.

This is not a guideline. It is the invariant that holds across every component, every platform, every domain that suctl touches.


When an operator asks suctl about nginx domains, suctl reads the nginx config tree. Not a database record of it. Not a cached file. Not a stored snapshot. The config tree — because that is what is real.

When an operator runs an Odoo operation, suctl-mod-odoo does not call Odoo from outside. During activation, its pre-activate hook glues a bridge into the running Odoo service via ExecStartPost. That bridge inherits Odoo’s virtualenv, user, environment, and PostgreSQL access. It does not talk to Odoo. It is Odoo — restricted to exactly what suctl-mod-odoo allows through the socket. suctl never touches the service again; removing the glue on deactivation is the module’s own cleanup. This pattern — a module creating or attaching to a socket inside an external service — is the module’s business, outside suctl’s boundary, and open to any module: suctl-mod-fail2ban attaches to fail2ban’s existing socket the same way. It is formalized in module lifecycle under External service sockets.

When the REPL shows system state, it reads it live. Every time. There is no background sync, no periodic refresh, no “last known state.” There is only what is true right now.

When a job is ephemeral, suctl says so honestly. In-memory job tracking is not a limitation — it is the correct model for state that does not survive a process restart, because the underlying operation doesn’t either.


Rejected approachWhy
Files storing nginx domain statusFiles lie when reality changes. The config tree cannot lie.
Database table for service stateA representation of truth is not truth.
Cached domain or service statusCache is always a representation. It can drift. Reality cannot.
External odoo-bin calls from suctlSpawns a parallel process with different context. suctl-odoo IS the context.
Tokens and API keys for authenticationStored secrets are a representation of trust. TLS domain identity is trust.
Heartbeat state stored permanentlyHeartbeat is live signal. Storing it converts presence into history.
”For performance” caching of system statePerformance does not justify lying to the operator.

Every rejection follows the same logic: the easy path stores a representation of reality. The correct path reads reality directly. The easy path is always rejected.


A capability is a purpose made usable — what suctl exposes, to the operator and to other modules. A capability is atomic or compound. Atomic and compound name the shape of a capability, not a different concept.

An atomic capability is a single operation that reads live state and produces a correct result. Managing a domain, restarting a service, installing an Odoo module — each is one operation: complete, independently correct, independently testable.

A compound capability composes operations to serve one purpose. Its steps may run in sequence or in parallel, branch on what each step reads, and self-correct from the live state returned along the way — the flow is governed by the operations’ real inputs and outputs, not by a fixed script. Every step reads reality fresh when it runs.

Composition is implementation, not a boundary. A compound capability calling its operations — or one capability calling another — is an internal detail, exactly like a function calling a function. It is never a violation; nothing needs to be “split.”

Exposure is the only line that matters. An operation used only inside one compound capability is private, like a private function. When another module needs it, you expose it — you make the private function public. The operation does not change; only its visibility does. An exposed operation is a capability.

The correctness guarantee: a compound capability cannot be more correct than the operations it composes. Because each operation reads reality fresh and is independently correct, the capability inherits that correctness. This is why atomic operations must be solid before compounds are built on them — not as process discipline, but as mathematical necessity.


suctl core handles orchestration and routing. Domain knowledge belongs in modules. Every domain — Odoo, nginx, fail2ban, Docker, Windows Services, network, and whatever follows — is a module. Core is the runtime that discovers modules, activates them, composes their capabilities, and presents their reality to the operator. Core’s own capabilities (system.module.*) are in-process — they are core, not a module.

The capability surface of any suctl installation is the exact capability surface of the server it runs on. Nothing assumed. Nothing configured that can drift from reality.

Modules are discovered, not declared. Core walks the module paths, checks each module’s requirements against the live system, and surfaces what is ready to activate. The operator activates explicitly. Core capabilities are always available — they need no activation because they are core.

Module config follows the .d convention. Each module drops its own file into /etc/suctl/conf.d/. Core reads the directory live. No monolithic config that grows without bound. Each module’s config is its own source of truth — readable, auditable, independently editable.

Module paths are ordered. System-approved modules live in /usr/lib/suctl/modules/. Third-party modules default to /usr/local/lib/suctl/modules/. Additional paths are declared in config. The same module name appearing in two paths is an error state — core stops and tells the operator what conflicts. The operator resolves by removing one conflicting installation.

The module system is itself an expression of the principle. Core does not store what modules are installed. It reads what is present on disk, checks requirements against live system state, and builds the capability surface fresh. The module directory is the truth. A database of installed modules would be a representation of it.


Odoo was the first domain because it was the need that produced suctl. It proved the model works. It is a first-class citizen.

Docker will follow. Other web servers will follow. Windows Server is planned. Developer workstations are planned. Each new domain joins as a first-class citizen by following the same principle — not by being bolted on as an afterthought.

The platform changes. The principle does not.

On Linux, nginx config tree is truth. On Windows, IIS config is truth. On any platform, the running system is the authority. No platform gets a caching exemption. No domain gets a representation exemption.

suctl-server does not assume what a node can do. It discovers capabilities at enrollment. Compound capabilities compose only what the target node actually has. A Windows node and a Linux node are both fleet members — different capabilities, same protocol, same principle.


The Linux kernel is open source. Every line is readable. Every major company in the world contributes to it. None of them control it, because Linus holds the principle — not as a written rule, but as a judgment applied consistently to every decision for decades.

The kernel does not lie about hardware. Every abstraction that pretends hardware behaves differently than it does is rejected, regardless of convenience.

suctl does not lie about the systems it manages. Every abstraction that pretends system state is different from what it actually is gets rejected, regardless of convenience.

This is why suctl is open source without concern. The code is readable. The principle is not transferable by reading. A fork that does not hold this principle is not suctl — it is a different tool wearing the same name.


The principle governs how suctl is built. It also governs how suctl is documented and how AI assistants are instructed to work on it.

All AI rules, commands, and skills live in .dotagent/ — one source, one location, one truth. They are symlinked into .claude/, .augment/, and any other agent directory. Not copied. Not maintained separately. Symlinked — because a copy can drift, and a copy that drifts is a representation that lies.

When the philosophy changes, every AI tool that touches this codebase sees the change simultaneously. No propagation step. No forgetting to update one tool while updating another. One source. No representations.

This is not process discipline. It is the principle applied recursively to its own documentation. If the documentation structure violated the principle, the principle would not be trustworthy. The structure proves the principle by following it.


Before any design decision, any code change, any new feature:

Does this read reality, or does it represent reality?

If it reads reality — proceed. If it represents reality — find the approach that reads it directly. If reality cannot be read directly in this case — that is worth understanding deeply before accepting any workaround.