Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 59 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,68 @@
# source-os

This is the main SociOS Linux SourceOS repository.
NixOS realization root for the SourceOS Linux control-plane stack.

## Role
## Enroll an M2

`source-os` is the Linux realization home for the SourceOS control-plane stack. It carries host roles, profiles, images, builders, and Linux-facing integration surfaces that realize the AgentPlane contract on Linux hosts.
```sh
# Phase A — install Asahi Linux (see docs/bootstrap/M2_ENROLL.md)
curl https://alx.sh | sh

## Current surfaces
# Phase B — replace Fedora with NixOS
curl -L https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | \
NIX_CHANNEL=nixos-unstable NO_REBOOT=1 bash
git clone https://github.com/SociOS-Linux/source-os.git /opt/sourceos/source-os
reboot

- `docs/repository-layout.md` — repository shape and intent
- `docs/agentplane-integration.md` — contract boundary with AgentPlane and shared standards
- `docs/mesh/` — mesh Linux estate integration planning, path-template mapping, and staged workstreams
- `linux/` — concrete Linux-facing templates for systemd-networkd, NetworkManager, nftables, and helper units
- `profiles/` / `modules/` — Nix realization surfaces
# Phase C — enroll (run as root from the repo root, ~35 min)
sudo bash scripts/enroll.sh

# Verify
bash scripts/doctor.sh
```

Full runbook: [`docs/bootstrap/M2_ENROLL.md`](docs/bootstrap/M2_ENROLL.md)

## What enrollment gives you

| Component | What it does |
|-----------|-------------|
| `sourceos-syncd` daemon | Polls local Katello every 5 min; applies NixOS updates; emits `SyncCycleReceipt` |
| `sourceos-boot` rollback | Auto-rolls back if post-boot health check fails |
| `harmonia` | Local Nix binary cache served at `http://127.0.0.1:8101` |
| Foreman+Katello | Local content lifecycle manager (Docker, linux/amd64 via qemu) |
| SOPS secrets | Katello password encrypted with device age key; never committed |

## Day-2 operations

```sh
# Check full stack health
bash scripts/doctor.sh

# Promote a new build to stable (triggers daemon sync within 5 min)
bash scripts/promote.sh --version <CV_VERSION>

# Daemon status
sourceos-syncd sync status

# Last sync receipt
sourceos-syncd receipts last

# Live daemon logs
journalctl -u sourceos-syncd -f
```

## Repository layout

- `hosts/builder-aarch64/` — M2 Asahi NixOS host config
- `modules/nixos/sourceos-syncd/` — NixOS module for the sync daemon
- `packages/sourceos-syncd/` / `packages/sourceos-boot/` — Nix derivations
- `scripts/enroll.sh` — one-shot M2 enrollment
- `scripts/doctor.sh` — full stack health check
- `scripts/promote.sh` — promote Katello content view to stable
- `scripts/katello-sourceos-setup.sh` — idempotent Katello org/product setup
- `docs/bootstrap/M2_ENROLL.md` — detailed enrollment runbook
- `profiles/` / `modules/` — shared NixOS profiles and modules

## Boundary rule

Expand Down
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

122 changes: 122 additions & 0 deletions scripts/promote.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/usr/bin/env bash
# Promote the latest (or a specific) sourceos-builder-aarch64 content view
# version through dev → candidate → stable in the local Katello instance.
#
# Usage:
# bash scripts/promote.sh # promote latest
# bash scripts/promote.sh --version 1.3 # promote specific version
# bash scripts/promote.sh --to stable # promote only to stable
# bash scripts/promote.sh --dry-run # print plan, do nothing
#
# After promotion, sourceos-syncd will detect the new stable version
# within SOURCEOS_POLL_INTERVAL seconds (default 300) and apply the update.

set -euo pipefail

KATELLO_URL="${FOREMAN_URL:-https://127.0.0.1:8443}"
KATELLO_USER="${FOREMAN_USER:-admin}"
ORG="${SOURCEOS_ORG:-SocioProphet}"
CV_NAME="${SOURCEOS_CV:-sourceos-builder-aarch64}"
TARGET_ENVS=("dev" "candidate" "stable")
CV_VERSION=""
DRY_RUN=0
KATELLO_PW_FILE="${SOURCEOS_DIR:-/etc/sourceos}/katello-admin-password"

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'

ok() { printf " ${GREEN}✓${NC} %s\n" "$*"; }
info() { printf " ${CYAN}·${NC} %s\n" "$*"; }
warn() { printf " ${YELLOW}!${NC} %s\n" "$*"; }
die() { printf " ${RED}✗ ERROR:${NC} %s\n" "$*" >&2; exit 1; }

usage() {
sed -n 's/^# //p' "$0" | head -12
exit 0
}

# ── Args ──────────────────────────────────────────────────────────────────────

while [[ $# -gt 0 ]]; do
case "$1" in
--version) CV_VERSION="$2"; shift 2 ;;
--to) TARGET_ENVS=("$2"); shift 2 ;;
--dry-run) DRY_RUN=1; shift ;;
--katello-url) KATELLO_URL="$2"; shift 2 ;;
--org) ORG="$2"; shift 2 ;;
--help|-h) usage ;;
*) die "Unknown argument: $1" ;;
esac
done

# ── Credentials ───────────────────────────────────────────────────────────────

if [[ -n "${FOREMAN_PASSWORD:-}" ]]; then
KATELLO_PASSWORD="${FOREMAN_PASSWORD}"
elif [[ -f "${KATELLO_PW_FILE}" ]]; then
KATELLO_PASSWORD=$(cat "${KATELLO_PW_FILE}")
else
die "Katello password not found. Set FOREMAN_PASSWORD or ensure ${KATELLO_PW_FILE} exists."
fi

# ── Hammer wrapper ────────────────────────────────────────────────────────────

hammer() {
docker exec katello-foreman hammer \
--server "${KATELLO_URL}" \
--username "${KATELLO_USER}" \
--password "${KATELLO_PASSWORD}" \
"$@"
}

# ── Discover version ──────────────────────────────────────────────────────────

if [[ -z "${CV_VERSION}" ]]; then
info "Querying latest content view version..."
CV_VERSION=$(hammer --output json content-view version list \
--organization "${ORG}" \
--content-view "${CV_NAME}" 2>/dev/null | \
python3 -c "
import json, sys
vs = json.load(sys.stdin)
if not vs: sys.exit(1)
print(sorted(vs, key=lambda v: v['ID'])[-1]['Version'])
") || die "Could not determine latest CV version — is Katello running? (docker ps)"
fi

info "Content view: ${CV_NAME}"
info "Version: ${CV_VERSION}"
info "Org: ${ORG}"
info "Promoting to: ${TARGET_ENVS[*]}"
[[ $DRY_RUN -eq 1 ]] && warn "DRY RUN — no changes will be made"

# ── Promote ───────────────────────────────────────────────────────────────────

echo

for env in "${TARGET_ENVS[@]}"; do
if [[ $DRY_RUN -eq 1 ]]; then
info "[dry-run] would promote v${CV_VERSION} → ${env}"
continue
fi

info "Promoting v${CV_VERSION} → ${env}..."
if hammer content-view version promote \
--organization "${ORG}" \
--content-view "${CV_NAME}" \
--version "${CV_VERSION}" \
--to-lifecycle-environment "${env}" 2>/dev/null; then
ok "v${CV_VERSION} → ${env}"
else
warn "Promotion to ${env} skipped (already at this version or previous env not promoted)"
fi
done

echo

if [[ $DRY_RUN -eq 0 ]]; then
POLL_INTERVAL="${SOURCEOS_POLL_INTERVAL:-300}"
ok "Done. sourceos-syncd will detect v${CV_VERSION} in stable within ${POLL_INTERVAL}s."
info "Force immediate check: systemctl restart sourceos-syncd"
info "Watch: journalctl -u sourceos-syncd -f"
info "Verify: sourceos-syncd sync status"
fi