docs(apex): limit public guidance to supported interfaces

This commit is contained in:
deytenit committed 2026-10-26 12:54:30 +03:00
1 parent 582fac434f
commit f24332b0b5
19 files changed
+57 -143

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
@@ -1,94 +0,0 @@
---
title: Composition replication
description: Patterns, ownership, and acceptance requirements for replicated applications.
weight: 28
---
Replication belongs to an application deployment across independently owned node
repositories. It does not clone a node or turn unrelated compositions into a
cluster. Each node pins commons, owns its secrets and storage, and runs APEX
commands locally.
This contract describes design requirements. It does not certify a deployment's
availability or provide a shared failover daemon, readiness adapter, or promotion
command. Those implementations and their evidence belong to the participating
applications and node repositories.
## Choose a pattern
| Pattern | Responsibility |
|---|---|
| Stateless copies | Deploy compatible configuration and route only to ready instances. |
| Replicated backends | Use application-supported shared state; each backend owns its consistency and recovery. |
| Application-native cluster | Follow the application's membership, replication, election, and recovery procedures. |
| Active instance with standby | Transfer state consistently and fence the previous writer before promotion. |
| Recovery-only deployment | Restore a verified backup and activate explicitly when live replication is not validated. |
A composition may combine patterns. Duplicate web instances do not imply that
duplicate schedulers or workers are safe. Do not copy live database directories
as a generic replication method. Backups remain necessary for corruption,
accidental deletion, and total group loss.
## Record the deployment contract
Keep the following non-secret information beside each participating composition:
- Group identity, members, failure domains, desired roles, and compatible versions.
- Every mutable state store, its write authority, replication method, and recovery procedure.
- Dependencies, private endpoints, required ports, and credentials referenced by location.
- Shared versus node-specific configuration and the order of coordinated updates.
- Readiness, ingress withdrawal, joining, draining, promotion, rejoining, and full-outage recovery.
- Recovery-time targets, possible acknowledged-data loss, and session or revocation behavior.
Distinct hostnames do not prove independent failure domains. Two application
instances depending on one unreplicated backend do not survive loss of that
backend. Secrets that must match across replicas stay outside Git; node-specific
credentials remain distinct.
## Network and lifecycle requirements
Validate the actual protocols over the existing private transport, including
peer identity, reconnects, port restrictions, and container-network replacement.
Node-local Docker names and bridge addresses do not provide cross-host discovery.
Do not introduce another network overlay as a side effect of replication setup.
Container health alone is insufficient: readiness must reflect required
dependencies and write authority. Traffic routing must not promote a database
or send application writes to an unpromoted standby. Multiple ingress paths to
one backend provide ingress redundancy, not backend replication.
Initial creation, joining, and ordinary restart are separate operations. An
ordinary restart must not replay a seed, create a replacement cluster, discard
existing data, or promote a replica. A returning former primary stays fenced
until reconciled or reseeded and accepted as a replica. Image rollback does not
automatically reverse schema changes.
## Controlled SSO promotion
The selected Daedalus–Metis pattern is active/standby with native asynchronous
database and session replication over existing Xray transport. Icarus is not a
member of this application group. No automatic promotion controller or election
service is implied by this pattern.
Before promotion, positively fence the previous writer. An unreachable SSH or
tunnel endpoint is not proof of fencing. Record replication positions, possible
lost changes, and the explicit native role changes. Preserve enrolled directory
data, account identifiers, groups, and application encryption secrets.
Recent account changes, sessions, or revocations can be lost with asynchronous
replication. Verify login, session reset, and stale-session rejection after
promotion; resetting application sessions does not revoke every downstream
token. Measure operator-driven recovery time rather than promising automatic
failover or zero data loss. Node-specific runbooks own the executable procedure.
## Acceptance evidence
For each claimed guarantee, exercise relevant process failures, loss of each
location, network partitions, stale members, rejoining, and rolling updates.
Observe end-user operations and dependency state. Record recovery time, lost
acknowledged data, conflicting writers, session behavior, and manual intervention.
Test full-group restoration separately.
Standalone host enrollment through `ci/scripts/init-node.py` ends before
application data migration or replication-group creation. It does not authorize
formatting disks or promoting writers during a composition operation.
@@ -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}
+1 -1
View File
@@ -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}
@@ -30,7 +30,7 @@ apex compose down # reverse order
apex compose restart
apex compose --dry-run up # print the commands only
apex compose -p apex restart traefik
apex compose -p sso up -d
apex compose -p app up -d
```
Put wrapper options (`-p` and `--dry-run`) before the action. Extra arguments pass
@@ -39,10 +39,9 @@ through to Docker Compose for the selected project. Each project's `.env` and
available as `apex compose restart apex traefik` or `apex compose up -d apex`;
prefer `-p` when arguments could also name a composition.
APEX does not unlink Xray sockets during Compose operations. Keep node-owned
cleanup in Xray's startup entrypoint; unrelated service restarts and unchanged
`up` operations can leave Xray running. When a mount or other container definition
changes, use targeted `up -d` to recreate it: `restart` retains the old definition.
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
@@ -77,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"
@@ -120,7 +120,7 @@ Two passes: (1) [anchor-IP](/apex/glossary#anchor-ip) validation over `compositi
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. No Compose action removes Xray socket paths; cleanup belongs to the node's Xray startup lifecycle.
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}
@@ -53,15 +53,14 @@ flowchart TD
## Модель «capture-up» {#capture-up}
Репозитории узлов служат реестрами состояния, а не просто источниками конфигурации: хост сам фиксирует то, что выполняет на практике.
Команда `apex sync/repository` фиксирует рабочее дерево и версию commons
в ветке `sync/<node>`, выполняет rebase на `origin/main` и отправляет ветку
с флагом `--force-with-lease`. Если в commons есть незакоммиченные изменения
или каталог `compositions/` отсутствует, команда отказывается работать.
Конфликт rebase прерывает синхронизацию.
1. Регулярная задача `sync/repository` на хосте коммитит рабочее дерево в ветку `sync/<node>` (включая указатель субмодуля commons), делает rebase поверх `origin/main` и выполняет push с флагом `--force-with-lease`.
2. Запланированное CI-задание в репозитории узла вливает ветку `sync/<node>` обратно в `main` от имени бота.
Благодаря циклу [«capture-up»](/apex/glossary#capture-up) любой дрейф конфигурации на хосте превращается в коммит: изменение наглядно отображается в diff и легко откатывается, исключая незаметные расхождения. Если субмодуль commons имеет незакоммиченные изменения («грязный») или каталог `compositions/` отсутствует, синхронизация прерывается без коммита, поэтому повреждённое состояние чекаута никогда не попадёт в историю.
> [!NOTE]
> CI-задание слияния клонирует репозиторий *без* субмодулей: субмодуль commons подключён по SSH-URL, недоступному стандартному токену CI, а самому слиянию требуется лишь обновить указатель gitlink.
Проверку и слияние этой ветки определяют правила репозитория. APEX не поставляет
CI-сценарий слияния и не требует определённого способа размещения репозиториев.
## Проектные ограничения {#constraints}
@@ -44,7 +44,7 @@ include:
|---|---|---|
| `xray` | xray | Прокси-шлюз с режимом host-network для анклава. |
| `adguard` | adguard | DNS-фильтрация в сетевом пространстве имён traefik; маршрутизируется по пути `/adguard` на FQDN узла. |
| `alloy` | alloy | Агент наблюдаемости на каждом узле (Grafana Alloy). Собирает метрики хоста и контейнеров, а также логи systemd journal и контейнеров, после чего **проталкивает** их исходящим потоком в центральные Prometheus и Loki (см. [Концепция: Модель безопасности](/apex/concepts/security-model#observability)). Имя хоста (`hostname`) контейнера совпадает с `APEX_NODE_HOST`, благодаря чему метки `node` и `instance` автоматически проставляются на всех метриках и потоках логов. |
| `alloy` | alloy | Необязательный агент Grafana Alloy. Отправляет метрики хоста и контейнеров, системный журнал и логи контейнеров на заданные совместимые конечные точки. См. [настройку наблюдаемости](/apex/concepts/security-model#observability). |
| `manual` | resticontainer | Одноразовый контейнер резервного копирования на основе меток (`ghcr.io/deytenit/resticontainer`). Никогда не запускается командой `up`; вызывается действием `backup/run`, которое определяет необходимость бэкапа каждого сервиса по compose-меткам `restic.*`. |
## Сети {#networks}
@@ -21,7 +21,7 @@ weight: 12
```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
```
### Имя хоста ОС
@@ -34,7 +34,11 @@ weight: 18
## Наблюдаемость {#observability}
Система мониторинга построена по модели **исходящей отправки (push)**, а не входящих опросов. Опциональный сервис `alloy` (Grafana Alloy, один агент на узел) собирает метрики хоста (аналог `node_exporter`), метрики контейнеров (cadvisor), системный журнал systemd journal и логи docker-контейнеров, после чего передаёт их в **центральные** хранилища Prometheus и Loki на выделенном узле. Метрики отправляются через `prometheus.remote_write` на адрес `APEX_PROM_PUSH_URL`, логи — через `loki.write` на `APEX_LOKI_PUSH_URL`. Обе конечные точки защищены базовой аутентификацией HTTP basic auth (`APEX_OBS_BASIC_AUTH_USER` / `APEX_OBS_BASIC_AUTH_PASS`). Поскольку трафик отправляется наружу, Alloy подключён к сети `direct` с доступом в интернет, а не к закрытой сети `enclave`. На самих узлах логи не накапливаются, и открывать внешний доступ к эндпоинтам сервисов не требуется.
Необязательный сервис `alloy` отправляет метрики хоста и контейнеров на адрес
`APEX_PROM_PUSH_URL`, а логи — на `APEX_LOKI_PUSH_URL`. Укажите совместимые
конечные точки и учётные данные HTTP basic auth в `APEX_OBS_BASIC_AUTH_USER`
и `APEX_OBS_BASIC_AUTH_PASS`. Сервису нужен исходящий доступ к этим адресам.
Размещение хранилищ и правила доступа определяет оператор.
## Изоляция секретов {#secrets}
+1 -1
View File
@@ -16,7 +16,7 @@ weight: 100
## Capture-up {#capture-up}
**Capture-up** — механизм фиксации состояния хоста в репозитории: регулярный коммит в ветку `sync/<node>`, который перебазируется на `main` и сливается средствами CI. В результате любой дрейф конфигурации становится наглядным diff, а не скрытым расхождением.
**Capture-up** — фиксация рабочего состояния хоста в ветке `sync/<node>` командой `apex sync/repository`. Проверку и слияние определяют правила репозитория.
## Commons {#commons}
@@ -30,7 +30,7 @@ apex compose down # обратный порядок
apex compose restart
apex compose --dry-run up # только напечатать команды
apex compose -p apex restart traefik
apex compose -p sso up -d
apex compose -p app up -d
```
Указывайте параметры APEX (`-p` и `--dry-run`) перед действием. Остальные аргументы
@@ -39,11 +39,9 @@ apex compose -p sso up -d
и `apex compose up -d apex` сохраняются; если имя сервиса или значение параметра
совпадает с именем композиции, выбирайте цель через `-p`.
APEX не удаляет сокеты Xray при операциях Compose. Очистка остаётся
в скрипте запуска Xray на узле: перезапуск другого сервиса или повторный `up`
могут оставить Xray работающим. После изменения монтирования или конфигурации
контейнера используйте `up -d` для нужной композиции; `restart` сохраняет прежнюю
конфигурацию контейнера.
Замените `app` именем композиции из своего репозитория. После изменения
монтирования или конфигурации контейнера используйте `up -d` для нужной
композиции; `restart` сохраняет прежнюю конфигурацию контейнера.
### Прямой запуск Compose для core
@@ -85,7 +83,7 @@ apex backup/run "$(cat <path-to-telegram-url-file>)"
apex sync/repository <telegram-bot-url>
```
Действие фиксирует изменения рабочей копии в ветке `sync/<node>`, выполняет rebase на `origin/main` и отправляет ветку через force-push с lease. Затем CI автоматически вливает изменения обратно в `main`. Этот механизм (capture-up) сохраняет актуальное состояние узла в репозиторий.
Действие фиксирует изменения рабочей копии в ветке `sync/<node>`, выполняет rebase на `origin/main` и отправляет ветку через force-push с lease. Этот механизм (capture-up) сохраняет актуальное состояние узла в репозиторий.
Действие прерывается с подробным сообщением, если подмодуль commons содержит незакоммиченные изменения или каталог `compositions/` отсутствует. При конфликтах rebase выполнение останавливается для ручного вмешательства. Подробнее архитектурная модель описана в разделе [Concept: Framework architecture](/apex/concepts/architecture#capture-up).
@@ -25,7 +25,7 @@ mkdir -p proprietaries/actions proprietaries/lib configs compositions/apex .gith
```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
```
@@ -46,7 +46,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
# цели push-наблюдаемости (только когда включён профиль alloy)
@@ -84,9 +84,12 @@ __pycache__/
Создайте файл `compositions/ADDRESSING.md` с подсетью узла (`APEX_SUBNET`) и схемой распределения адресов: диапазоны `.10`–`.19` зарезервированы за базовыми сервисами (core), а `.20` и выше выделяются под проекты. Все назначаемые в будущем [якорные IP](/apex/glossary#anchor-ip) фиксируются в этом документе.
### Подключите CI capture-up
### Определите правила проверки синхронизации
Настройте периодический CI-workflow, который автоматически вливает ветку `sync/<node>` в `main` (если ветка существует) с авторством бота. Шаг checkout должен выполняться **без** загрузки подмодулей: слияние только сдвигает указатель `commons`, а SSH-ссылка на подмодуль недоступна по стандартному CI-токену. Полный цикл описан в разделе [Concept: Framework architecture](/apex/concepts/architecture) ([capture-up](/apex/glossary#capture-up)).
Решите, как проверять и вливать ветку `sync/<node>`, которую создаёт
`apex sync/repository`. Для автоматического слияния настройте разрешения
репозитория и доступ к субмодулям под выбранный сценарий.
См. [модель capture-up](/apex/concepts/architecture#capture-up).
### Проверьте работу
@@ -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"
@@ -120,7 +120,7 @@ weight: 32
Без цели обходит сначала core, затем остальные композиции по алфавиту; для `down` порядок обратный. Параметр `-p` выбирает одну композицию или `all`. Команда `up` по умолчанию добавляет `-d`; файлы `.env` и `apex.env` подключаются в этом порядке, вычисленные значения узла и путей передаются через окружение. Параметры APEX ставятся перед действием, остальные аргументы передаются Docker Compose. Флаг `--dry-run` печатает команды без запуска.
Сохраняются формы `apex compose restart apex traefik` и `apex compose up -d apex`. Используйте `-p`, чтобы явно отличить композицию от сервиса или значения параметра. Команда не удаляет сокеты Xray: очисткой управляет скрипт запуска Xray на узле.
Сохраняются формы `apex compose restart apex traefik` и `apex compose up -d apex`. Используйте `-p`, чтобы явно отличить композицию от сервиса или значения параметра.
## env {#env}