4.5 KiB
title, description, weight
| title | description | weight |
|---|---|---|
| Framework architecture | Four-pillar node repositories, the commons submodule, and the dispatch flow. | 11 |
apex splits fleet automation into one shared repository — the commons — and one repository per node. The commons carries everything every node shares: the engine, the shared actions, and the core composition. A node repository carries everything that makes that node itself: its identity, host configs, service compositions, and node-local proprietaries.
The four pillars
Every node repository has the same shape:
{{< filetree/container >}} {{< filetree/folder name="." state="open" >}} {{< filetree/folder name="commons" state="closed" >}}{{< /filetree/folder >}} {{< filetree/folder name="proprietaries" state="closed" >}}{{< /filetree/folder >}} {{< filetree/folder name="configs" state="closed" >}}{{< /filetree/folder >}} {{< filetree/folder name="compositions" state="closed" >}}{{< /filetree/folder >}} {{< filetree/file name="node.env" >}} {{< filetree/file name="init.sh" >}} {{< /filetree/folder >}} {{< /filetree/container >}}
| Pillar | Content | Ownership |
|---|---|---|
commons/ |
Git submodule → apex.ermnvldmr.com, pinned to a release tag |
Shared, versioned |
proprietaries/ |
Node-local actions, libraries, and tool sources (overlaid over commons) | Node |
configs/ |
Host configuration sources: ufw/, cron/crontab, systemd/, crowdsec/ |
Node |
compositions/ |
One directory per project plus the apex/ core wrapper and the addressing ledger |
Node |
The submodule pin is the framework's version contract: a node runs exactly the engine and core composition recorded in its gitlink. Upgrading a node is a deliberate act — check out a newer tag inside commons/ and commit the moved pointer.
Dispatch flow
The launcher commons/apex acts on the local checkout; there is no node argument anywhere.
flowchart TD
A["apex <group>/<action> [args]"] --> B{Layout}
B -- "commons/ dir with node.env beside it" --> C[Node checkout:<br/>overlay = proprietaries/actions]
B -- otherwise --> D[Standalone commons:<br/>no overlay]
C --> E["Overlay resolve<br/>(proprietaries first)"]
D --> E
E --> F["Identity resolve<br/>(FQDN + node.env)"]
F --> G["Build ctx<br/>(log, sys, tpl, host, ufw,<br/>crowdsec, systemd, notify)"]
G --> H["parse args (Meta)"]
H --> I["run(ctx, args)"]
The engine detects the layout from its own location: running from a directory named commons/ whose parent contains node.env means "node checkout" — the parent becomes the repository root and proprietaries/actions/ becomes the overlay tree. Any other location is a standalone commons checkout acting as its own root.
Listing (apex --help) never imports action modules; summaries and disabled markers are extracted by static ast scanning. Dispatch loads exactly one module by path. Details in Concept: Actions and the overlay.
The capture-up model
apex sync/repository records the working tree and commons pin in a
sync/<node> branch, rebases onto origin/main, and pushes with
--force-with-lease. It refuses to run when commons has uncommitted changes
or the compositions/ directory is missing. A rebase conflict aborts the sync.
Review and integration of that branch are repository policies. APEX does not supply a CI merge workflow or require a particular repository hosting setup.
Design constraints
- Stdlib only. The engine runs on a stock Debian Python 3 — no
pip, no third-party imports. The optionalvendor/directory exists for pinned pure-Python code but ships empty. - Hand-authored composes. There is no manifest layer or code generation; compose files are the source of truth.
- Evidence over convention. Anything enforceable is enforced by an action (anchor-IP lint, crontab rollback, marker-block self-healing) rather than by documentation.
See also: