diff --git a/skills/apsuflow/SKILL.md b/skills/apsuflow/SKILL.md index 461b8de..e5eca24 100644 --- a/skills/apsuflow/SKILL.md +++ b/skills/apsuflow/SKILL.md @@ -46,11 +46,15 @@ in the same change when applicable requirements or CLI behavior change. profiles contain only host configuration and service scripts. The daemon never detects a distribution, libc, or init system. Before opening its listener it must prove its unprivileged effective UID and create/remove a child beneath a - writable delegated cgroup v2 path exposing `cpu`, `memory`, and `pids`. + writable delegated cgroup v2 parent exposing `cpu`, `memory`, and `pids` while + the daemon itself remains in a leaf cgroup. On systemd profiles this requires + `Delegate=yes` plus `ProtectControlGroups=private`; Apsuflow then moves itself + into `/daemon` before proving or creating workload children. `doctor` reports the same kernel-observed parent-process and cgroup facts. - Failure is closed: there is no root or service-manager-specific fallback. The - current runit profile also passed its complete acceptance path on Void Linux - musl; that is evidence for the capability contract, not a distro special case. + Failure is closed: there is no root or service-manager-specific fallback. + Both release-supported profiles have complete acceptance proof, including the + Void-musl/runit PID-1 path in a local KVM/QEMU VM with a real OCI workload, + restart, limits, and encrypted backup/restore. - `.apsu` contains desired workloads and infrastructure, never secret values or mutable runtime state. - Services are continuously reconciled. Jobs are finite run-to-completion work. @@ -76,15 +80,22 @@ that drift before making another change. ## Install and update Use only the exact stable tag and artifact named by `docs/getting-started.md`. -Verify `SHA256SUMS` and its SSH signature before installing; never use a mutable -`latest` URL. Keep the previous executable and state backup until the new one +Official bootstrap and Cloud-Init entry points require that exact version, +verify `SHA256SUMS` with the repository-pinned SSH release identity, verify the +selected archive hash, replace atomically, and retain `apsuflow.previous`; never +use a mutable `latest` URL. Keep the previous executable and state backup until the new one passes `version`, `doctor`, workload diagnosis, and each operator-facing route. -For a private image registry, pipe the credential into `apsuflow registry login - --username --password-stdin`; do not put it in `.apsu` or shell -history. Login stores sealed `_registry/{host}` state, and joined agents receive -only their per-node-resealed pull credential during assignment. Confirm the next -pull succeeds; `unauthorized` means the registry host or credential is wrong, not -that the workload should be stopped first. +For a private image registry, pipe the exact registry credential in its +`username:password` form into `apsuflow registry login --password-stdin`; +the command has no separate username flag. Do not put the credential in `.apsu`, +process arguments, shell history, a runtime auth file, or host-side container-tool +configuration. Login stores sealed `_registry/{host}` state, and joined agents +receive only their per-node-resealed pull credential during assignment. Establish +and verify this login before the first apply, then confirm the digest-pinned pull +and workload health through Apsuflow. If a known-good credential still yields +`unauthorized`, stop: that is an Apsuflow credential-delivery defect, not permission +to use `skopeo login`, copy an OCI archive, invoke the runtime, or otherwise bypass +the control plane. For host-network workloads, confirm the old container released its host ports and the successor is the sole running generation; repeated replacement failures are @@ -112,10 +123,12 @@ host paths—also require `apply --acknowledge-recovery-risk`; read the reported source location and consequence before accepting it. Verify the actual route or job result; a successful apply alone proves only admission. -The daemon normally needs root for host effects. Do not therefore run every -client command with `sudo`: protect the daemon data directory and use an operator -context or token. Never make bootstrap tokens or the data directory broadly -readable. `server --no-auth` is acceptable only for disposable loopback use. +The server daemon runs unprivileged in its delegated cgroup; the agent remains +rootful only for its authorized runtime and host-network effects. Do not run +client commands with `sudo`: protect daemon state and use an operator context or +token. Join credentials are node-bound, role-bound, single-use, and expire after +15 minutes; never reuse, commit, or broaden them. `server --no-auth` is +acceptable only for disposable loopback use. ## Multiple environments @@ -188,11 +201,13 @@ job backup { ## Cluster and network -Start the first server on a stable address. Create a short-lived role-scoped join -token with `apsuflow token create --name NODE --role server`, then start another -server with `apsuflow server --peer FIRST:7946 --token TOKEN`. For an agent, -create an `agent` token and run `apsuflow agent --join FIRST:7654 --token TOKEN`. -Never reuse or commit a bootstrap token. +Start the first server on a stable address. Create a 15-minute, single-use join +credential bound to the exact node ID with `apsuflow token create --name NODE +--role server`, then start that server with `apsuflow server --peer FIRST:7946 +--token TOKEN`. For an agent, mint the same node-bound credential with `--role +agent` and run `apsuflow agent --node-id NODE --join FIRST:7654 --token TOKEN`. +The `--node-id` value must exactly match the credential name. Never reuse or +commit a join credential. Allow between nodes only required control-plane traffic: @@ -241,6 +256,10 @@ apsuflow status --context recovery This backs up Apsuflow control-plane state, not bind-mounted application data, databases, object stores, or brokers. Back those up natively and test both paths. +Restore replaces authentication state and removes the empty target's bootstrap +token because it cannot match restored credential hashes. Retain an original +administrator credential outside the lost server data directory; without one, +recovery is blocked rather than silently granting new access. Never mutate `store.db`, WAL files, generations, assignments, certificates, or sealed values with SQLite/SQL. Never invoke crun/youki or host-network tools to