Initial commit: VaultMesh Skills collection

Collection of operational skills for VaultMesh infrastructure including:
- backup-sovereign: Backup and recovery operations
- btc-anchor: Bitcoin anchoring
- cloudflare-tunnel-manager: Cloudflare tunnel management
- container-registry: Container registry operations
- disaster-recovery: Disaster recovery procedures
- dns-sovereign: DNS management
- eth-anchor: Ethereum anchoring
- gitea-bootstrap: Gitea setup and configuration
- hetzner-bootstrap: Hetzner server provisioning
- merkle-forest: Merkle tree operations
- node-hardening: Node security hardening
- operator-bootstrap: Operator initialization
- proof-verifier: Cryptographic proof verification
- rfc3161-anchor: RFC3161 timestamping
- secrets-vault: Secrets management

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Vault Sovereign
2025-12-27 00:25:00 +00:00
commit eac77ef7b4
213 changed files with 11724 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/_common.sh"
main() {
need age
need sops
need mkdir
need chmod
need stat
need grep
need sed
need date
log_info "Preflight OK."
}
main "$@"

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/_common.sh"
: "${VAULT_ROOT:=~/infrastructure/vault}"
: "${AGE_KEY_DIR:=~/.config/sops/age}"
: "${AGE_KEYS_FILE:=~/.config/sops/age/keys.txt}"
main() {
vr="$(expand_path "$VAULT_ROOT")"
kd="$(expand_path "$AGE_KEY_DIR")"
kf="$(expand_path "$AGE_KEYS_FILE")"
echo "[PLAN] $(date -Iseconds) secrets-vault (age+sops)"
echo "[PLAN] VAULT_ROOT: $vr"
echo "[PLAN] AGE_KEY_DIR: $kd"
echo "[PLAN] AGE_KEYS_FILE: $kf"
echo
echo "[PLAN] Will ensure age identity exists (keys.txt)."
echo "[PLAN] Will write vault/.sops.yaml and encrypted templates under vault/secrets/."
echo "[PLAN] Will NOT print secret plaintext."
echo
echo "[PLAN] Next: export DRY_RUN=0 && ./scripts/11_apply.sh"
}
main "$@"

View File

@@ -0,0 +1,130 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
source "$SCRIPT_DIR/_common.sh"
: "${VAULT_ROOT:=~/infrastructure/vault}"
: "${AGE_KEY_DIR:=~/.config/sops/age}"
: "${AGE_KEYS_FILE:=~/.config/sops/age/keys.txt}"
: "${RECIPIENTS_FILE:=$SKILL_ROOT/outputs/recipients.txt}"
: "${BACKUP_DIR:=$SKILL_ROOT/outputs/backups}"
backup_file() {
local f="$1"
mkdir -p "$BACKUP_DIR"
if [[ -f "$f" ]]; then
ts="$(date -Iseconds | tr ':' '-')"
cp -p "$f" "$BACKUP_DIR/$(basename "$f").${ts}.bak"
fi
}
main() {
confirm_gate
mkdir -p "$SKILL_ROOT/outputs" "$BACKUP_DIR"
vr="$(expand_path "$VAULT_ROOT")"
kd="$(expand_path "$AGE_KEY_DIR")"
kf="$(expand_path "$AGE_KEYS_FILE")"
mkdir -p "$vr/secrets"
mkdir -p "$kd"
# 1) ensure age identity exists
if [[ ! -f "$kf" ]]; then
log_info "Generating age identity: $kf"
age-keygen -o "$kf" >/dev/null
chmod 600 "$kf"
else
log_info "Using existing age identity: $kf"
chmod 600 "$kf" || true
fi
# extract recipient (public key)
recipient="$(grep -E '^# public key: ' "$kf" | head -n1 | sed 's/^# public key: //')"
[[ -n "$recipient" ]] || die "Could not parse public key from $kf"
echo "$recipient" > "$RECIPIENTS_FILE"
# 2) write .sops.yaml policy
policy="$vr/.sops.yaml"
backup_file "$policy"
cat > "$policy" <<EOF
creation_rules:
- path_regex: secrets/.*\\.enc\\.(yaml|yml|json|env)$
encrypted_regex: '^(data|stringData|secrets|values)$'
age: ["$recipient"]
EOF
# 3) scaffold encrypted templates
# plaintext templates are generated into a temp file then immediately encrypted and removed.
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
# cloudflare
cat > "$tmpdir/cloudflare.yaml" <<EOF
data:
account_id: "REPLACE_ME"
tunnel_token: "REPLACE_ME"
api_token: "REPLACE_ME"
EOF
sops --encrypt --age "$recipient" "$tmpdir/cloudflare.yaml" > "$vr/secrets/cloudflare.enc.yaml"
# gitea
cat > "$tmpdir/gitea.yaml" <<EOF
data:
admin_username: "REPLACE_ME"
admin_password: "REPLACE_ME"
admin_email: "REPLACE_ME"
secret_key: "REPLACE_ME"
EOF
sops --encrypt --age "$recipient" "$tmpdir/gitea.yaml" > "$vr/secrets/gitea.enc.yaml"
# registry
cat > "$tmpdir/registry.yaml" <<EOF
data:
htpasswd: "REPLACE_ME"
tls_cert_pem: "REPLACE_ME"
tls_key_pem: "REPLACE_ME"
EOF
sops --encrypt --age "$recipient" "$tmpdir/registry.yaml" > "$vr/secrets/registry.enc.yaml"
# k8s
cat > "$tmpdir/k8s.yaml" <<EOF
data:
kubeconfig: "REPLACE_ME"
cluster_token: "REPLACE_ME"
EOF
sops --encrypt --age "$recipient" "$tmpdir/k8s.yaml" > "$vr/secrets/k8s.enc.yaml"
# 4) vault README
readme="$vr/README.md"
backup_file "$readme"
cat > "$readme" <<EOF
# Vault (age + sops)
This folder stores **encrypted secrets** for GitOps.
## Edit a secret
\`\`\`bash
sops secrets/cloudflare.enc.yaml
\`\`\`
## Decrypt to stdout (careful)
\`\`\`bash
sops -d secrets/cloudflare.enc.yaml
\`\`\`
## Policy
See \`.sops.yaml\`. Only files matching \`secrets/*.enc.(yaml|json|env)\` are covered.
## Key location
- age identity: \`$kf\` (0600)
- public recipient: stored in \`outputs/recipients.txt\` by the skill
EOF
echo "$vr" > "$SKILL_ROOT/outputs/vault_root.txt"
log_info "Vault forged at: $vr"
log_info "Recipient: $recipient"
}
main "$@"

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
source "$SCRIPT_DIR/_common.sh"
: "${VAULT_ROOT:=~/infrastructure/vault}"
: "${AGE_KEYS_FILE:=~/.config/sops/age/keys.txt}"
: "${RECIPIENTS_FILE:=$SKILL_ROOT/outputs/recipients.txt}"
main() {
vr="$(expand_path "$VAULT_ROOT")"
kf="$(expand_path "$AGE_KEYS_FILE")"
status="$SKILL_ROOT/outputs/status_matrix.json"
ok_keys=false; ok_perm=false; ok_policy=false; ok_cipher=false
[[ -f "$kf" ]] && ok_keys=true
if [[ -f "$kf" ]]; then
perm="$(stat -c '%a' "$kf" 2>/dev/null || echo "")"
[[ "$perm" == "600" ]] && ok_perm=true
fi
[[ -f "$vr/.sops.yaml" ]] && ok_policy=true
if [[ -f "$vr/secrets/cloudflare.enc.yaml" && -f "$vr/secrets/gitea.enc.yaml" && -f "$vr/secrets/registry.enc.yaml" && -f "$vr/secrets/k8s.enc.yaml" ]]; then
ok_cipher=true
fi
blockers="[]"
if [[ "$ok_keys" != "true" ]]; then blockers='["missing_age_identity"]'
elif [[ "$ok_policy" != "true" ]]; then blockers='["missing_sops_policy"]'
elif [[ "$ok_cipher" != "true" ]]; then blockers='["missing_encrypted_templates"]'
fi
cat > "$status" <<EOF
{
"skill":"secrets-vault",
"timestamp":"$(date -Iseconds)",
"vault_root":"$(json_escape "$vr")",
"checks":[
{"name":"age_identity_present", "ok": $ok_keys, "path":"$(json_escape "$kf")"},
{"name":"age_identity_perm_600", "ok": $ok_perm},
{"name":"sops_policy_present", "ok": $ok_policy, "path":"$(json_escape "$vr/.sops.yaml")"},
{"name":"encrypted_templates_present", "ok": $ok_cipher}
],
"blockers": $blockers,
"warnings": [],
"next_steps": ["commit vault ciphertext to git", "cloudflare-tunnel-manager", "container-registry"]
}
EOF
log_info "Wrote $status"
cat "$status"
}
main "$@"

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
source "$SCRIPT_DIR/_common.sh"
main() {
status="$SKILL_ROOT/outputs/status_matrix.json"
report="$SKILL_ROOT/outputs/audit_report.md"
vault_root="$(cat "$SKILL_ROOT/outputs/vault_root.txt" 2>/dev/null || echo "")"
cat > "$report" <<EOF
# Secrets Vault Audit Report (age + sops)
**Generated:** $(date -Iseconds)
**Vault Root:** \`$vault_root\`
**Skill Version:** 1.0.0
## Status Matrix
$(if [[ -f "$status" ]]; then echo '```json'; cat "$status"; echo '```'; else echo "_Missing status_matrix.json_"; fi)
## Operating Rules
- Commit **only ciphertext** (\`*.enc.*\`) to git.
- Never store plaintext secrets in the repo.
- Keep \`~/.config/sops/age/keys.txt\` at **0600**.
- Keep an offline recovery copy of the age identity.
## EU Compliance
EU (Ireland - Dublin), Irish jurisdiction. Secrets remain local-first; git stores ciphertext only.
EOF
log_info "Wrote $report"
cat "$report"
}
main "$@"

View File

@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail
log_info(){ echo "[INFO] $(date -Iseconds) $*"; }
log_warn(){ echo "[WARN] $(date -Iseconds) $*" >&2; }
log_error(){ echo "[ERROR] $(date -Iseconds) $*" >&2; }
die(){ log_error "$*"; exit 1; }
need(){ command -v "$1" >/dev/null 2>&1 || die "Missing required tool: $1"; }
expand_path() {
local p="$1"
# shellcheck disable=SC2086
eval echo "$p"
}
confirm_gate() {
: "${DRY_RUN:=1}"
: "${REQUIRE_CONFIRM:=1}"
: "${CONFIRM_PHRASE:=I UNDERSTAND THIS WILL CREATE A SECRETS VAULT}"
[[ "$DRY_RUN" == "0" ]] || die "DRY_RUN=$DRY_RUN (set DRY_RUN=0)."
if [[ "$REQUIRE_CONFIRM" == "1" ]]; then
echo "Type to confirm:"
echo " $CONFIRM_PHRASE"
read -r input
[[ "$input" == "$CONFIRM_PHRASE" ]] || die "Confirmation phrase mismatch."
fi
}
json_escape() {
local s="$1"
s="${s//\\/\\\\}"; s="${s//\"/\\\"}"; s="${s//$'\n'/\\n}"
printf "%s" "$s"
}

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SKILL_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
source "$SKILL_ROOT/scripts/_common.sh"
: "${BACKUP_DIR:=$SKILL_ROOT/outputs/backups}"
main() {
confirm_gate
if [[ ! -d "$BACKUP_DIR" ]]; then
log_warn "No backups found; nothing to restore."
exit 0
fi
log_warn "Backups exist in: $BACKUP_DIR"
log_warn "This rollback restores only files we backed up (.sops.yaml, README.md)."
log_warn "Encrypted templates are safe to keep; remove manually if desired."
echo "Listing backups:"
ls -1 "$BACKUP_DIR" || true
log_info "Rollback is conservative by design. Restore manually by copying desired *.bak to target."
}
main "$@"