provisioning-catalog/playbooks/bootstrap_initial/playbook.ncl

142 lines
6.5 KiB
Text
Raw Permalink Normal View History

let pb = import "schemas/lib/playbook.ncl" in
pb.make_playbook {
id = "bootstrap_initial",
name = "Initial Bootstrap",
description = "First-time full platform bootstrap. Builds buildkit-runner golden image off-platform, deploys libre-wuji with temporary bootstrap zot, migrates images to wuji S3-backed zot, deploys libre-daoshi (CI/build cluster), deploys ops-vm (signing), and bootstraps Radicle governance repos. Run exactly once per platform instantiation.",
version = 1,
preconditions = [
"Hetzner Cloud API token available and project created",
"DigitalOcean API token available for ops-vm",
"S3 bucket for zot (libre-wuji-zot) exists with versioning enabled",
"NATS account server configured and account JWTs available",
"Domain DNS delegated (for ingress on wuji/daoshi)",
"SOPS age key generated and accessible",
"Radicle identity (rad auth) initialized for bootstrapping operator",
"Bootstrap zot: local registry or any reachable OCI registry for initial image push",
"Ed25519 key pair generated for initial ops-controller, keeper, and radicle delegate",
],
params = [
{ name = "workspace", description = "Primary workspace (must be libre-wuji)", required = true, default_val = "libre-wuji" },
{ name = "hetzner_context", description = "hcloud context name", required = true, default_val = "" },
{ name = "do_context", description = "DigitalOcean context (for ops-vm)", required = true, default_val = "" },
{ name = "bootstrap_zot_url", description = "Temporary OCI registry for image bootstrap", required = true, default_val = "" },
{ name = "s3_bucket", description = "S3 bucket for wuji zot", required = true, default_val = "libre-wuji-zot" },
{ name = "ops_vm_host", description = "ops-vm SSH host (once provisioned)", required = false, default_val = "" },
{ name = "ops_controller_key", description = "Path to ops-controller Ed25519 private key PEM", required = true, default_val = "" },
{ name = "keeper_key", description = "Path to keeper Ed25519 private key PEM", required = true, default_val = "" },
{ name = "initial_delegate_did", description = "Radicle DID of first governance delegate", required = true, default_val = "" },
{ name = "skip_golden_image", description = "Skip golden image build if already pushed", required = false, default_val = "false" },
],
steps = [
{
id = "verify_prerequisites",
name = "Verify all bootstrap prerequisites",
script = "steps/verify_prerequisites.nu",
dry_run_arg = "--dry-run",
on_error = 'Stop,
depends_on = [],
},
{
id = "build_golden_image",
name = "Build buildkit-runner golden image off-platform",
script = "steps/build_golden_image.nu",
dry_run_arg = "--dry-run",
on_error = 'Stop,
depends_on = ["verify_prerequisites"],
},
{
id = "push_to_bootstrap_zot",
name = "Push golden image to bootstrap registry",
script = "steps/push_to_bootstrap_zot.nu",
dry_run_arg = "--dry-run",
on_error = 'Stop,
depends_on = ["build_golden_image"],
},
{
id = "deploy_libre_wuji",
name = "Deploy libre-wuji cluster (using bootstrap zot)",
script = "steps/deploy_libre_wuji.nu",
dry_run_arg = "--dry-run",
on_error = 'Stop,
depends_on = ["push_to_bootstrap_zot"],
},
{
id = "verify_wuji_zot_live",
name = "Wait for wuji S3-backed zot to be healthy",
script = "steps/verify_wuji_zot_live.nu",
dry_run_arg = "--dry-run",
on_error = 'Stop,
depends_on = ["deploy_libre_wuji"],
},
{
id = "migrate_to_wuji_zot",
name = "Migrate images from bootstrap registry to wuji zot",
script = "steps/migrate_to_wuji_zot.nu",
dry_run_arg = "--dry-run",
on_error = 'Stop,
depends_on = ["verify_wuji_zot_live"],
},
{
id = "bootstrap_radicle_governance",
name = "Create initial Radicle governance repos and set delegation",
script = "steps/bootstrap_radicle_governance.nu",
dry_run_arg = "--dry-run",
on_error = 'Stop,
depends_on = ["verify_wuji_zot_live"],
},
{
id = "deploy_libre_daoshi",
name = "Deploy libre-daoshi CI/build cluster",
script = "steps/deploy_libre_daoshi.nu",
dry_run_arg = "--dry-run",
on_error = 'Continue,
depends_on = ["migrate_to_wuji_zot", "bootstrap_radicle_governance"],
},
{
id = "deploy_ops_vm",
name = "Deploy ops-vm on DigitalOcean (signing isolation)",
script = "steps/deploy_ops_vm.nu",
dry_run_arg = "--dry-run",
on_error = 'Continue,
depends_on = ["bootstrap_radicle_governance"],
},
{
id = "verify_end_to_end",
name = "Run end-to-end ops pipeline smoke test",
script = "steps/verify_end_to_end.nu",
dry_run_arg = "--dry-run",
on_error = 'Continue,
depends_on = ["deploy_libre_daoshi", "deploy_ops_vm"],
},
{
id = "emit_audit",
name = "Emit bootstrap-complete audit event",
script = "steps/emit_audit.nu",
params = { event_type = "bootstrap_complete" },
dry_run_arg = "--dry-run",
on_error = 'Continue,
depends_on = ["verify_end_to_end"],
},
],
rollback_strategy = 'manual,
success_criteria = [
"libre-wuji cluster running with all components healthy (zot, ops-controller, radicle-seed, observability)",
"zot serving images from S3 bucket — bootstrap registry no longer needed",
"Radicle repos created: policy-libre-wuji, desired-libre-wuji, state-libre-wuji, policy-libre-daoshi, desired-libre-daoshi, state-libre-daoshi",
"Initial delegation set (M=1 threshold) written to all policy repos",
"libre-daoshi cluster running with forgejo, woodpecker, postgresql, radicle-seed",
"ops-vm running on DigitalOcean AMS3 with keeper-daemon and radicle-seed",
"End-to-end: test op submitted via NATS → signed by keeper → applied → audit event in SurrealDB",
"ops history shows bootstrap-complete audit event",
],
emit_audit = true,
adr_refs = ["adr-037", "adr-038", "adr-039"],
}