docs(apex): clarify public operations and configuration (#69)
This commit is contained in:
1 parent
47c70471e1
commit
cff22c88dd
18 files changed
+155
-52
No files matched your search
@@ -53,15 +53,13 @@ Listing (`apex --help`) never imports action modules; summaries and disabled ma
|
||||
|
||||
## The capture-up model {#capture-up}
|
||||
|
||||
Node repositories are ledgers, not just sources: the host itself records what it runs.
|
||||
`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.
|
||||
|
||||
1. A scheduled `sync/repository` run on the host commits the working tree to a `sync/<node>` branch (including the commons submodule pointer), rebases it onto `origin/main`, and pushes with `--force-with-lease`.
|
||||
2. A scheduled CI job in the node repository merges `sync/<node>` back into `main` under a bot identity.
|
||||
|
||||
This [capture-up](/apex/glossary#capture-up) loop means drift on a host becomes a commit — visible, diffable, and revertable — instead of silent divergence. The sync aborts rather than committing when the commons submodule is dirty or the `compositions/` tree is missing, so a broken checkout never becomes the recorded state.
|
||||
|
||||
> [!NOTE]
|
||||
> The CI merge job checks out the repository *without* submodules: the commons submodule uses an SSH URL that the default CI token cannot fetch, and the merge only moves the gitlink pointer anyway.
|
||||
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 {#constraints}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ Opt-in services, gated by compose profiles a node enables in `COMPOSE_PROFILE
|
||||
|---|---|---|
|
||||
| `xray` | xray | Host-network proxy edge for the enclave. |
|
||||
| `adguard` | adguard | DNS filtering, sharing traefik's network namespace; routed at `/adguard` on the node FQDN. |
|
||||
| `alloy` | alloy | Per-node observability agent (Grafana Alloy). Collects host + container metrics and journal + container logs and **pushes** them outbound to a central Prometheus and Loki (see [Concept: Security model](/apex/concepts/security-model#observability)). Its container `hostname` is set to `APEX_NODE_HOST`, and that value stamps the `node` and `instance` labels on every metric and log stream. |
|
||||
| `alloy` | alloy | Optional Grafana Alloy agent. Sends host/container metrics and journal/container logs to configured compatible endpoints. See [observability configuration](/apex/concepts/security-model#observability). |
|
||||
| `manual` | resticontainer | One-shot label-driven backup runner (`ghcr.io/deytenit/resticontainer`) — never starts with `up`; invoked by the `backup/run` action, which discovers per-service backup intent from `restic.*` compose labels. |
|
||||
|
||||
## Networks {#networks}
|
||||
|
||||
@@ -21,7 +21,7 @@ It declares identity explicitly through these keys:
|
||||
```ini {filename="node.env"}
|
||||
APEX_NODE_FQDN=node1.example.com
|
||||
APEX_NODE_HOST=node1.example.com
|
||||
APEX_SUBNET=198.18.16.0/24
|
||||
APEX_SUBNET=198.18.0.0/24
|
||||
```
|
||||
|
||||
### The OS hostname
|
||||
|
||||
@@ -34,7 +34,11 @@ The [core composition](/apex/glossary#core-composition) encodes container-level
|
||||
|
||||
## Observability {#observability}
|
||||
|
||||
Observability is an **outbound push**, not an exposed route. The optional `alloy` core service (Grafana Alloy, one agent per node) collects host metrics (a `node_exporter`-equivalent), container metrics (cadvisor), the systemd journal, and docker container logs, and pushes them to a **central** Prometheus and Loki that live on a designated node — the metrics via `prometheus.remote_write` to `APEX_PROM_PUSH_URL`, the logs via `loki.write` to `APEX_LOKI_PUSH_URL`. Both targets are authenticated with HTTP basic auth using `APEX_OBS_BASIC_AUTH_USER` / `APEX_OBS_BASIC_AUTH_PASS`. Because the push is outbound, Alloy attaches to the `direct` network (which has egress) rather than the `internal` `enclave`; there is no node-local log store and no `/loki` route to protect.
|
||||
The optional `alloy` service sends host and container metrics to
|
||||
`APEX_PROM_PUSH_URL` and logs to `APEX_LOKI_PUSH_URL`. Configure compatible
|
||||
endpoints and HTTP basic-auth credentials through `APEX_OBS_BASIC_AUTH_USER`
|
||||
and `APEX_OBS_BASIC_AUTH_PASS`. The service requires outbound connectivity to
|
||||
those endpoints; their hosting and access policy are deployment choices.
|
||||
|
||||
## Secrets boundary {#secrets}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ Terminology used within the apex fleet operations framework.
|
||||
|
||||
## Capture-up {#capture-up}
|
||||
|
||||
**Capture-up** - The flow that records a host's live state back into its repository: a scheduled commit to the `sync/<node>` branch, rebased onto `main` and folded back by CI. Drift becomes a diff instead of divergence.
|
||||
**Capture-up** — Recording a host's working state in a `sync/<node>` branch through `apex sync/repository`. The repository's own policy determines review and integration.
|
||||
|
||||
## Commons {#commons}
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ weight: 24
|
||||
|
||||
| Step | Effect |
|
||||
|---|---|
|
||||
| `configure/base` | Ensures a Debian base: package index update, essential packages (`curl`, `wget`, `git`, `rsyslog`), rsyslog running, `ufw` installed, and the `ufw-docker` helper at `~/.local/bin/ufw-docker`. |
|
||||
| `configure/base` | Installs missing Debian packages after checking that APT's simulation contains no upgrades or removals, ensures rsyslog is running, and verifies the pinned `/usr/local/bin/ufw-docker` before privileged execution. Avoid concurrent package operations. |
|
||||
| `configure/timezone` | Applies `configs/timezone` when present and refreshes active cron after a timezone change. Supports `--dry-run`. |
|
||||
| `configure/ufw` | Applies host rules, injects the managed kernel-rule blocks, applies container rules via `ufw-docker`. Supports `--dry-run`. |
|
||||
| `configure/crowdsec` | Installs CrowdSec if absent, deploys `configs/crowdsec/` to `/etc/crowdsec/`, installs collections, and the firewall bouncer when configured. Supports `--dry-run`. |
|
||||
| `configure/cron` | Renders `configs/cron/crontab` through the template layer and installs it — with preview, empty-render refusal (exit `65`), and rollback on a failed install. Supports `--dry-run`. |
|
||||
@@ -27,10 +28,39 @@ After `configure/ufw`, restart `ufw` deliberately (`sudo systemctl restart ufw`)
|
||||
apex compose up # core first, then services alphabetically
|
||||
apex compose down # reverse order
|
||||
apex compose restart
|
||||
apex compose up --dry-run # print the commands only
|
||||
apex compose --dry-run up # print the commands only
|
||||
apex compose -p apex restart traefik
|
||||
apex compose -p app up -d
|
||||
```
|
||||
|
||||
Extra arguments pass through to `docker compose` per project. Each project's `.env` and `apex.env` are supplied automatically when present.
|
||||
Put wrapper options (`-p` and `--dry-run`) before the action. Extra arguments pass
|
||||
through to Docker Compose for the selected project. Each project's `.env` and
|
||||
`apex.env` are supplied automatically when present. Positional targeting remains
|
||||
available as `apex compose restart apex traefik` or `apex compose up -d apex`;
|
||||
prefer `-p` when arguments could also name a composition.
|
||||
|
||||
Replace `app` with a composition defined in your repository. When a mount or
|
||||
other container definition changes, use targeted `up -d` to recreate it:
|
||||
`restart` retains the existing definition.
|
||||
|
||||
### Direct core Compose commands
|
||||
|
||||
```bash
|
||||
apex env --sync
|
||||
cd compositions/apex
|
||||
docker compose config --quiet
|
||||
docker compose restart traefik
|
||||
```
|
||||
|
||||
Sync preserves existing secret and UID/GID text in `.env` and its tier-storage
|
||||
symlink. A managed block loads committed `apex.env` settings, including profiles,
|
||||
then synthesized node/path values. Writes use mode `0600`. Resync after changing
|
||||
those settings or paths; do not hand-edit the managed block. Docker's shell
|
||||
environment still overrides file values.
|
||||
|
||||
`eval "$(apex env)"`, `apex env --dotenv`, and `apex env --write generated.env`
|
||||
export or save generated APEX values only. They do not load composition secrets
|
||||
or replace the full `--sync` bridge. Output modes are mutually exclusive.
|
||||
|
||||
## Backups {#backups}
|
||||
|
||||
@@ -46,7 +76,7 @@ Runs [resticontainer](https://github.com/deytenit/resticontainer) one-shot under
|
||||
apex sync/repository <telegram-bot-url>
|
||||
```
|
||||
|
||||
Commits the live checkout to `sync/<node>`, rebases onto `origin/main`, and force-pushes with lease; CI folds the branch back into `main`. The action refuses to run — with a paged error — when the commons submodule is dirty or the `compositions/` tree is missing, and aborts cleanly on a rebase conflict (manual intervention required). See [Concept: Framework architecture](/apex/concepts/architecture#capture-up) for the model.
|
||||
Commits the live checkout to `sync/<node>`, rebases onto `origin/main`, and force-pushes with lease. The action refuses to run — with a paged error — when the commons submodule is dirty or the `compositions/` tree is missing, and aborts cleanly on a rebase conflict (manual intervention required). See [Concept: Framework architecture](/apex/concepts/architecture#capture-up) for the model.
|
||||
|
||||
## Update awareness {#updates}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ The gitlink pins the exact engine version the node runs. Upgrading later mean
|
||||
```ini {filename="node.env"}
|
||||
APEX_NODE_FQDN=node1.example.com
|
||||
APEX_NODE_HOST=node1.example.com
|
||||
APEX_SUBNET=198.18.16.0/24
|
||||
APEX_SUBNET=198.18.0.0/24
|
||||
APEX_GIT_AUTHOR_NAME=apex [bot]
|
||||
APEX_GIT_AUTHOR_EMAIL=apex@localhost
|
||||
```
|
||||
@@ -41,7 +41,7 @@ include:
|
||||
```
|
||||
|
||||
```ini {filename="compositions/apex/apex.env"}
|
||||
APEX_TRAEFIK_DIRECT_IP=198.18.16.10
|
||||
APEX_TRAEFIK_DIRECT_IP=198.18.0.10
|
||||
APEX_RESTIC_REPOSITORY=s3:<endpoint>/<bucket>/<node>
|
||||
COMPOSE_PROFILES=adguard,xray,alloy
|
||||
# observability push targets (only when the alloy profile is enabled)
|
||||
@@ -77,9 +77,12 @@ __pycache__/
|
||||
|
||||
Create `compositions/ADDRESSING.md` with the node's subnet (`APEX_SUBNET`) and the reserved ranges (`.10`–`.19` core, `.20`+ services). Every [anchor IP](/apex/glossary#anchor-ip) allocated later is recorded here.
|
||||
|
||||
### Wire capture-up CI
|
||||
### Choose a sync review policy
|
||||
|
||||
Add a scheduled workflow that merges `sync/<node>` into `main` when the branch exists, committing under a bot identity. Check out **without** submodules — the merge only moves the commons pointer, and an SSH submodule URL is not fetchable with the default CI token. See [Concept: Framework architecture](/apex/concepts/architecture) for the full [capture-up](/apex/glossary#capture-up) loop.
|
||||
Decide how to review and integrate the `sync/<node>` branch produced by
|
||||
`apex sync/repository`. If you automate integration, configure repository
|
||||
permissions and submodule access for that workflow. See the
|
||||
[capture-up model](/apex/concepts/architecture#capture-up).
|
||||
|
||||
### Validate
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ services:
|
||||
- ./@tier2/<anchor>/data:/data
|
||||
networks:
|
||||
direct:
|
||||
ipv4_address: 198.18.16.20
|
||||
ipv4_address: 198.18.0.20
|
||||
enclave: {}
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
|
||||
@@ -4,7 +4,7 @@ description: "Every commons action: arguments and behavior."
|
||||
weight: 32
|
||||
---
|
||||
|
||||
The sixteen [commons](/apex/glossary#commons) actions, by group. Arguments are shown as the parser accepts them; optional arguments are bracketed. Exit codes follow the [engine convention](/apex/reference/cli#exit-codes).
|
||||
The [commons](/apex/glossary#commons) actions, by group. Arguments are shown as the parser accepts them; optional arguments are bracketed. Exit codes follow the [engine convention](/apex/reference/cli#exit-codes).
|
||||
|
||||
## configure {#configure}
|
||||
|
||||
@@ -12,13 +12,19 @@ The sixteen [commons](/apex/glossary#commons) actions, by group. Arguments are
|
||||
|
||||
`apex configure [--dry-run]`
|
||||
|
||||
Runs the whole suite in fixed order — `base`, `ufw`, `crowdsec`, `cron`, `systemd`, `routing` — and stops at the first failing step, propagating its exit code. `--dry-run` is forwarded to the steps that support it.
|
||||
Runs the suite in order: `base`, `timezone`, `ufw`, `crowdsec`, `cron`, `systemd`, `routing`, then additional node actions. A failing step contributes a nonzero exit status; later steps may still run. `--dry-run` is forwarded only to steps that support it, so it is not a guarantee that the entire umbrella command is read-only.
|
||||
|
||||
### configure/base
|
||||
|
||||
`apex configure/base`
|
||||
|
||||
Debian-only guard (exit `1` elsewhere). Updates the package index, installs the essential packages (`curl`, `wget`, `git`, `rsyslog`), ensures rsyslog is running, installs `ufw` when absent, and downloads the `ufw-docker` helper to `~/.local/bin/ufw-docker` (skipped when already present).
|
||||
Debian-only guard (exit `1` elsewhere). Checks installed package state for `curl`, `wget`, `git`, `rsyslog`, and `ufw`; installs missing packages and ensures rsyslog is running. Refuses an APT simulation containing upgrades or removals and uses `--no-upgrade --no-remove`. Avoid concurrent package operations: simulation and installation are separate transactions. Verifies the immutable revision and SHA-256 of `/usr/local/bin/ufw-docker` before running it as root. Unsafe paths and unverified downloads are rejected; an executable file alone is not trusted.
|
||||
|
||||
### configure/timezone
|
||||
|
||||
`apex configure/timezone [--dry-run]`
|
||||
|
||||
Applies the IANA zone named in `configs/timezone` and refreshes active cron after a change. An absent declaration preserves the current timezone; an invalid zone fails before changing it.
|
||||
|
||||
### configure/ufw
|
||||
|
||||
@@ -110,9 +116,19 @@ Two passes: (1) [anchor-IP](/apex/glossary#anchor-ip) validation over `compositi
|
||||
|
||||
### compose
|
||||
|
||||
`apex compose <action> [--dry-run] [extra...]`
|
||||
`apex compose [-p <composition>] [--dry-run] <action> [extra...]`
|
||||
|
||||
Orchestrates `docker compose <action>` across the node: the core project first, then services alphabetically (reversed for `down`). `up` implies `-d`; `.env`/`apex.env` are passed per project when present; extra arguments pass through; `--dry-run` (accepted in any position) prints commands instead of executing.
|
||||
Without a target, visits core first and other compositions alphabetically, reversed for `down`. `-p` selects one composition or `all`; `up` defaults to detached mode. `.env` then `apex.env` are passed per project, with generated identity/path values in the process environment. Remaining arguments pass through to Docker Compose. Wrapper options precede the action; `--dry-run` prints commands without executing them.
|
||||
|
||||
Compatibility forms include `apex compose restart apex traefik` and `apex compose up -d apex`. Use explicit `-p` to distinguish a composition from a service or option value.
|
||||
|
||||
## env {#env}
|
||||
|
||||
`apex env [--export | --dotenv | --write <path> | --sync]`
|
||||
|
||||
Default output is shell-quoted APEX exports for `eval "$(apex env)"`. `--dotenv` emits dotenv syntax; `--write` writes generated values to a separate file, including a relative filename. These modes do not read composition credentials or merge `apex.env`.
|
||||
|
||||
`--sync` preserves existing `compositions/apex/.env` text and updates only its managed block: committed `apex.env` settings first, generated identity/path values last. This includes non-APEX settings such as `COMPOSE_PROFILES` and `TZ`, allowing bare Docker Compose commands in the core directory. Existing tier symlinks remain intact, and synchronized files use mode `0600`. Resync after changing source settings or tier paths. Malformed managed markers fail without rewriting uncertain content. Shell environment values still have Docker Compose precedence.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user