- Institutional / JVMTM / reserve-provenance / GRU transport + standards JSON - Validation and verify scripts (Blockscout labels, x402, GRU preflight, P1 local path) - Wormhole wiring in AGENTS, MCP_SETUP, MASTER_INDEX, 04-configuration README - Meta docs, integration gaps, live verification log, architecture updates - CI validate-config workflow updates Operator/LAN items, submodule working trees, and public token-aggregation edge routes remain follow-up (see TODOS_CONSOLIDATED P1). Made-with: Cursor
390 lines
14 KiB
Bash
Executable File
390 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Validate the DBIS identity completion package.
|
|
# Usage:
|
|
# bash scripts/validation/validate-dbis-identity-package.sh \
|
|
# --package config/production/dbis-identity-public-did-package.json \
|
|
# --secrets config/production/dbis-identity-public-did-secrets.env
|
|
#
|
|
# For template validation only:
|
|
# bash scripts/validation/validate-dbis-identity-package.sh \
|
|
# --package config/production/dbis-identity-public-did-package.example.json \
|
|
# --secrets config/production/dbis-identity-public-did-secrets.example.env \
|
|
# --allow-placeholders
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
PACKAGE_PATH="$PROJECT_ROOT/config/production/dbis-identity-public-did-package.json"
|
|
SECRETS_PATH="$PROJECT_ROOT/config/production/dbis-identity-public-did-secrets.env"
|
|
ALLOW_PLACEHOLDERS=false
|
|
PARTIAL_EXTERNAL_ALLOWED=false
|
|
|
|
log_info() { echo "[INFO] $1"; }
|
|
log_ok() { echo "[OK] $1"; }
|
|
log_warn() { echo "[WARN] $1"; }
|
|
log_err() { echo "[ERROR] $1"; }
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--package)
|
|
PACKAGE_PATH="$2"
|
|
shift 2
|
|
;;
|
|
--secrets)
|
|
SECRETS_PATH="$2"
|
|
shift 2
|
|
;;
|
|
--allow-placeholders)
|
|
ALLOW_PLACEHOLDERS=true
|
|
shift
|
|
;;
|
|
*)
|
|
log_err "Unknown argument: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
ERRORS=0
|
|
WARNINGS=0
|
|
|
|
require_file() {
|
|
local path="$1"
|
|
if [[ -f "$path" ]]; then
|
|
log_ok "Found: $path"
|
|
else
|
|
log_err "Missing file: $path"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
}
|
|
|
|
check_placeholder_string() {
|
|
local label="$1"
|
|
local value="$2"
|
|
if [[ -z "$value" ]]; then
|
|
log_err "$label is empty"
|
|
ERRORS=$((ERRORS + 1))
|
|
return
|
|
fi
|
|
if [[ "$value" == *"<fill-me"* || "$value" == "CHANGEME" || "$value" == "TODO" ]]; then
|
|
if $ALLOW_PLACEHOLDERS; then
|
|
log_warn "$label still contains a placeholder"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
else
|
|
log_err "$label still contains a placeholder"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
else
|
|
log_ok "$label is populated"
|
|
fi
|
|
}
|
|
|
|
check_placeholder_string_maybe_partial() {
|
|
local label="$1"
|
|
local value="$2"
|
|
if $PARTIAL_EXTERNAL_ALLOWED; then
|
|
if [[ -z "$value" ]]; then
|
|
log_warn "$label is empty while package is awaiting external endorser data"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
return
|
|
fi
|
|
if [[ "$value" == *"<fill-me"* || "$value" == "CHANGEME" || "$value" == "TODO" ]]; then
|
|
log_warn "$label still contains a placeholder while package is awaiting external endorser data"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
return
|
|
fi
|
|
log_ok "$label is populated"
|
|
return
|
|
fi
|
|
check_placeholder_string "$label" "$value"
|
|
}
|
|
|
|
check_indy_did_format() {
|
|
local label="$1"
|
|
local value="$2"
|
|
if [[ "$value" == *"<fill-me"* || "$value" == "CHANGEME" || "$value" == "TODO" ]]; then
|
|
if $ALLOW_PLACEHOLDERS || $PARTIAL_EXTERNAL_ALLOWED; then
|
|
log_warn "$label still contains a placeholder"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
else
|
|
log_err "$label still contains a placeholder"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
return
|
|
fi
|
|
if [[ -z "$value" ]]; then
|
|
if $ALLOW_PLACEHOLDERS || $PARTIAL_EXTERNAL_ALLOWED; then
|
|
log_warn "$label is empty while package is awaiting external endorser data"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
else
|
|
log_err "$label is empty"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
return
|
|
fi
|
|
if [[ "$value" =~ ^[1-9A-HJ-NP-Za-km-z]{16,32}$ ]]; then
|
|
log_ok "$label format looks like an Indy DID"
|
|
else
|
|
if $PARTIAL_EXTERNAL_ALLOWED; then
|
|
log_warn "$label does not yet look like a valid Indy DID"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
else
|
|
log_err "$label does not look like a valid Indy DID"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
fi
|
|
}
|
|
|
|
check_uuid_like() {
|
|
local label="$1"
|
|
local value="$2"
|
|
if [[ "$value" == *"<fill-me"* || "$value" == "CHANGEME" || "$value" == "TODO" ]]; then
|
|
if $ALLOW_PLACEHOLDERS || $PARTIAL_EXTERNAL_ALLOWED; then
|
|
log_warn "$label still contains a placeholder"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
else
|
|
log_err "$label still contains a placeholder"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
return
|
|
fi
|
|
if [[ -z "$value" ]]; then
|
|
if $ALLOW_PLACEHOLDERS || $PARTIAL_EXTERNAL_ALLOWED; then
|
|
log_warn "$label is empty while package is awaiting external endorser data"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
else
|
|
log_err "$label is empty"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
return
|
|
fi
|
|
if [[ "$value" =~ ^[0-9a-fA-F-]{16,}$ ]]; then
|
|
log_ok "$label format looks connection-id compatible"
|
|
else
|
|
if $PARTIAL_EXTERNAL_ALLOWED; then
|
|
log_warn "$label does not yet look like a valid connection identifier"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
else
|
|
log_err "$label does not look like a valid connection identifier"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
fi
|
|
}
|
|
|
|
check_change_control_ref() {
|
|
local value="$1"
|
|
if [[ "$value" =~ ^DBIS-ID-GOV-[0-9]{4}-[0-9]{3}$ ]]; then
|
|
log_ok "governance.changeControlRef format is valid"
|
|
else
|
|
log_err "governance.changeControlRef must match DBIS-ID-GOV-YYYY-NNN"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
}
|
|
|
|
check_quorum_format() {
|
|
local value="$1"
|
|
if [[ "$value" =~ ^[0-9]+-of-[0-9]+$ ]]; then
|
|
log_ok "governance.endorserGovernanceModel.quorum format is valid"
|
|
else
|
|
log_err "governance.endorserGovernanceModel.quorum must match N-of-M"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
}
|
|
|
|
check_env_var() {
|
|
local label="$1"
|
|
local name="$2"
|
|
local value="${!name:-}"
|
|
if [[ -z "$value" ]]; then
|
|
if $ALLOW_PLACEHOLDERS; then
|
|
log_warn "$label env var $name is empty"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
else
|
|
log_err "$label env var $name is empty"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
return
|
|
fi
|
|
check_placeholder_string "$label env var $name" "$value"
|
|
}
|
|
|
|
require_file "$PACKAGE_PATH"
|
|
require_file "$SECRETS_PATH"
|
|
|
|
if ! command -v jq >/dev/null 2>&1; then
|
|
log_err "jq is required"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $ERRORS -gt 0 ]]; then
|
|
exit 1
|
|
fi
|
|
|
|
if jq -e '
|
|
(.schemaVersion | type == "string") and
|
|
(.programId | type == "string") and
|
|
(.packageStatus | type == "string") and
|
|
(.ariesAgent.adminUrl | type == "string") and
|
|
(.ariesAgent.didcommUrl | type == "string") and
|
|
(.ariesAgent.walletType | type == "string") and
|
|
(.ariesAgent.adminAuthMode | type == "string") and
|
|
(.ariesAgent.adminApiKeyEnv | type == "string") and
|
|
(.ledger.type | type == "string") and
|
|
(.ledger.targetNetwork | type == "string") and
|
|
(.ledger.trustScope | type == "string") and
|
|
(.ledger.poolName | type == "string") and
|
|
(.ledger.genesisSource | type == "string") and
|
|
(.ledger.didMethod | type == "string") and
|
|
(.ledger.nymWriteMode | type == "string") and
|
|
(.governance.governanceVersion | type == "string") and
|
|
(.governance.changeControlRef | type == "string") and
|
|
(.governance.changeControlFormat | type == "string") and
|
|
(.governance.operatorOwner | type == "string") and
|
|
(.governance.approvalOwner | type == "string") and
|
|
(.governance.endorserGovernanceModel.type | type == "string") and
|
|
(.governance.endorserGovernanceModel.quorum | type == "string") and
|
|
(.governance.endorserGovernanceModel.custodians | type == "array") and
|
|
(.governance.endorserGovernanceModel.custodians | length >= 3) and
|
|
(.governance.endorserGovernanceModel.singleKeyDidControl | type == "string") and
|
|
(.governance.endorserGovernanceModel.currentPhase | type == "string") and
|
|
(.governance.endorserGovernanceModel.futurePhases | type == "array") and
|
|
(.governance.endorserGovernanceModel.futurePhases | length >= 1) and
|
|
(.roles.author.alias | type == "string") and
|
|
(.roles.author.connectionIdEnv | type == "string") and
|
|
(.roles.endorser.alias | type == "string") and
|
|
(.roles.endorser.did | type == "string") and
|
|
(.roles.endorser.connectionIdEnv | type == "string") and
|
|
(.anoncreds.schemas | type == "array") and
|
|
(.anoncreds.schemas | length >= 1) and
|
|
(.anoncreds.verificationProfiles | type == "array") and
|
|
(.anoncreds.verificationProfiles | length >= 1) and
|
|
(.evidence.outputDir | type == "string") and
|
|
(.evidence.requiredArtifacts | type == "array") and
|
|
(.evidence.requiredArtifacts | length >= 1)
|
|
' "$PACKAGE_PATH" >/dev/null; then
|
|
log_ok "Package JSON structure is valid"
|
|
else
|
|
log_err "Package JSON structure is invalid"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
|
|
PACKAGE_STATUS="$(jq -r '.packageStatus' "$PACKAGE_PATH")"
|
|
if [[ "$PACKAGE_STATUS" == "awaiting-external-endorser" ]]; then
|
|
PARTIAL_EXTERNAL_ALLOWED=true
|
|
log_info "Package status allows external-governance gaps to remain warnings"
|
|
fi
|
|
|
|
check_placeholder_string "schemaVersion" "$(jq -r '.schemaVersion' "$PACKAGE_PATH")"
|
|
check_placeholder_string "programId" "$(jq -r '.programId' "$PACKAGE_PATH")"
|
|
check_placeholder_string "ariesAgent.adminUrl" "$(jq -r '.ariesAgent.adminUrl' "$PACKAGE_PATH")"
|
|
check_placeholder_string "ariesAgent.didcommUrl" "$(jq -r '.ariesAgent.didcommUrl' "$PACKAGE_PATH")"
|
|
check_placeholder_string "ariesAgent.adminAuthMode" "$(jq -r '.ariesAgent.adminAuthMode' "$PACKAGE_PATH")"
|
|
check_placeholder_string "ledger.targetNetwork" "$(jq -r '.ledger.targetNetwork' "$PACKAGE_PATH")"
|
|
check_placeholder_string "ledger.trustScope" "$(jq -r '.ledger.trustScope' "$PACKAGE_PATH")"
|
|
check_placeholder_string "ledger.poolName" "$(jq -r '.ledger.poolName' "$PACKAGE_PATH")"
|
|
check_placeholder_string "ledger.genesisSource" "$(jq -r '.ledger.genesisSource' "$PACKAGE_PATH")"
|
|
check_placeholder_string "ledger.didMethod" "$(jq -r '.ledger.didMethod' "$PACKAGE_PATH")"
|
|
check_placeholder_string "ledger.nymWriteMode" "$(jq -r '.ledger.nymWriteMode' "$PACKAGE_PATH")"
|
|
check_placeholder_string "governance.governanceVersion" "$(jq -r '.governance.governanceVersion' "$PACKAGE_PATH")"
|
|
CHANGE_CONTROL_REF="$(jq -r '.governance.changeControlRef' "$PACKAGE_PATH")"
|
|
check_placeholder_string "governance.changeControlRef" "$CHANGE_CONTROL_REF"
|
|
check_change_control_ref "$CHANGE_CONTROL_REF"
|
|
check_placeholder_string "governance.changeControlFormat" "$(jq -r '.governance.changeControlFormat' "$PACKAGE_PATH")"
|
|
check_placeholder_string "governance.operatorOwner" "$(jq -r '.governance.operatorOwner' "$PACKAGE_PATH")"
|
|
check_placeholder_string "governance.approvalOwner" "$(jq -r '.governance.approvalOwner' "$PACKAGE_PATH")"
|
|
check_placeholder_string "governance.endorserGovernanceModel.type" "$(jq -r '.governance.endorserGovernanceModel.type' "$PACKAGE_PATH")"
|
|
GOV_QUORUM="$(jq -r '.governance.endorserGovernanceModel.quorum' "$PACKAGE_PATH")"
|
|
check_placeholder_string "governance.endorserGovernanceModel.quorum" "$GOV_QUORUM"
|
|
check_quorum_format "$GOV_QUORUM"
|
|
check_placeholder_string "governance.endorserGovernanceModel.singleKeyDidControl" "$(jq -r '.governance.endorserGovernanceModel.singleKeyDidControl' "$PACKAGE_PATH")"
|
|
check_placeholder_string "governance.endorserGovernanceModel.currentPhase" "$(jq -r '.governance.endorserGovernanceModel.currentPhase' "$PACKAGE_PATH")"
|
|
if jq -e '(.governance.endorserGovernanceModel.custodians | type == "array") and (.governance.endorserGovernanceModel.custodians | length >= 3)' "$PACKAGE_PATH" >/dev/null; then
|
|
log_ok "governance.endorserGovernanceModel.custodians has at least 3 entries"
|
|
else
|
|
log_err "governance.endorserGovernanceModel.custodians must have at least 3 entries"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
if jq -e '(.governance.endorserGovernanceModel.futurePhases | type == "array") and (.governance.endorserGovernanceModel.futurePhases | length >= 1)' "$PACKAGE_PATH" >/dev/null; then
|
|
log_ok "governance.endorserGovernanceModel.futurePhases is populated"
|
|
else
|
|
log_err "governance.endorserGovernanceModel.futurePhases must contain at least one entry"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
check_placeholder_string "roles.author.alias" "$(jq -r '.roles.author.alias' "$PACKAGE_PATH")"
|
|
AUTHOR_PUBLIC_DID="$(jq -r '.roles.author.publicDid' "$PACKAGE_PATH")"
|
|
ENDORSER_DID="$(jq -r '.roles.endorser.did' "$PACKAGE_PATH")"
|
|
|
|
check_placeholder_string_maybe_partial "roles.author.publicDid" "$AUTHOR_PUBLIC_DID"
|
|
check_placeholder_string_maybe_partial "roles.author.verkey" "$(jq -r '.roles.author.verkey' "$PACKAGE_PATH")"
|
|
check_placeholder_string "roles.endorser.alias" "$(jq -r '.roles.endorser.alias' "$PACKAGE_PATH")"
|
|
check_placeholder_string_maybe_partial "roles.endorser.did" "$ENDORSER_DID"
|
|
check_placeholder_string "anoncreds.schemas[0].name" "$(jq -r '.anoncreds.schemas[0].name' "$PACKAGE_PATH")"
|
|
check_placeholder_string "anoncreds.schemas[0].version" "$(jq -r '.anoncreds.schemas[0].version' "$PACKAGE_PATH")"
|
|
|
|
if [[ -n "$AUTHOR_PUBLIC_DID" ]]; then
|
|
check_indy_did_format "roles.author.publicDid" "$AUTHOR_PUBLIC_DID"
|
|
fi
|
|
|
|
if [[ -n "$ENDORSER_DID" && "$ENDORSER_DID" != *"<fill-me"* ]]; then
|
|
check_indy_did_format "roles.endorser.did" "$ENDORSER_DID"
|
|
fi
|
|
|
|
GENESIS_SOURCE="$(jq -r '.ledger.genesisSource' "$PACKAGE_PATH")"
|
|
if [[ "$GENESIS_SOURCE" == /* ]]; then
|
|
if [[ -f "$GENESIS_SOURCE" ]]; then
|
|
log_ok "genesisSource file exists: $GENESIS_SOURCE"
|
|
else
|
|
log_warn "genesisSource file not present on this machine: $GENESIS_SOURCE"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
fi
|
|
fi
|
|
|
|
set -a
|
|
source "$SECRETS_PATH"
|
|
set +a
|
|
|
|
AUTHOR_ENV_NAME="$(jq -r '.roles.author.connectionIdEnv' "$PACKAGE_PATH")"
|
|
ENDORSER_ENV_NAME="$(jq -r '.roles.endorser.connectionIdEnv' "$PACKAGE_PATH")"
|
|
ADMIN_KEY_ENV_NAME="$(jq -r '.ariesAgent.adminApiKeyEnv' "$PACKAGE_PATH")"
|
|
ADMIN_AUTH_MODE="$(jq -r '.ariesAgent.adminAuthMode' "$PACKAGE_PATH")"
|
|
|
|
if [[ "$ADMIN_AUTH_MODE" == "apiKey" ]]; then
|
|
check_env_var "Configured admin API key" "$ADMIN_KEY_ENV_NAME"
|
|
else
|
|
log_info "Skipping admin API key requirement because adminAuthMode=$ADMIN_AUTH_MODE"
|
|
fi
|
|
|
|
if [[ -n "${!AUTHOR_ENV_NAME:-}" ]]; then
|
|
check_env_var "Author connection" "$AUTHOR_ENV_NAME"
|
|
check_uuid_like "Author connection" "${!AUTHOR_ENV_NAME}"
|
|
else
|
|
log_warn "Author connection env var $AUTHOR_ENV_NAME is empty"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
fi
|
|
|
|
if [[ -n "${!ENDORSER_ENV_NAME:-}" ]]; then
|
|
check_env_var "Endorser connection" "$ENDORSER_ENV_NAME"
|
|
check_uuid_like "Endorser connection" "${!ENDORSER_ENV_NAME}"
|
|
else
|
|
if $PARTIAL_EXTERNAL_ALLOWED; then
|
|
log_warn "Endorser connection env var $ENDORSER_ENV_NAME is empty while package is awaiting external endorser data"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
else
|
|
log_err "Endorser connection env var $ENDORSER_ENV_NAME is empty"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
fi
|
|
|
|
if [[ $ERRORS -gt 0 ]]; then
|
|
log_err "Identity package validation failed with $ERRORS error(s) and $WARNINGS warning(s)"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $WARNINGS -gt 0 ]]; then
|
|
log_warn "Identity package validation passed with $WARNINGS warning(s)"
|
|
else
|
|
log_ok "Identity package validation passed"
|
|
fi
|