From cdf8fd9d17682001da7e6952d2067f0efe097197 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 2 Sep 2026 05:36:00 -0400 Subject: [PATCH 1/2] Add inactive Control foundation roll-up --- README.md | 19 +- RESTORE.md | 12 + ci/required-files.txt | 4 + control/v1/control-policy-set.json | 1 + .../test/control-foundation-rollup.test.sh | 272 ++++++++++++++++++ 5 files changed, 304 insertions(+), 4 deletions(-) create mode 100644 control/v1/control-policy-set.json create mode 100755 scripts/test/control-foundation-rollup.test.sh diff --git a/README.md b/README.md index 697a502..5c57cf1 100644 --- a/README.md +++ b/README.md @@ -63,15 +63,26 @@ and an external-target smoke remain required. ## Inactive control policy-set validator -`control/v1/` defines a canonical identity bundle for six later Control foundation -policies: duty separation, sandbox, credentials, risk gates, kill switch, and -immutable evidence. Its validator checks exact immutable policy and decision refs; -it does not contain or evaluate those policies. +`control/v1/` defines the canonical shape and order for six Control foundation +policies: credentials, duty separation, immutable evidence, kill switch, risk +gates, and sandbox. Its validator checks the set shape and relations. It does not +contain or evaluate those policies. The package stays inactive and fail-closed. It grants no authority, activates no profile, reads no credential, launches no adapter, and performs no external write. Later bounded units own each policy body and its enforcement. +## Inactive Control foundation roll-up + +`control/v1/control-policy-set.json` pins the six shipped policy and decision files +in the required order. It also pins their shared core contract generation and +package. The focused test recomputes all twelve file digests and the core package +closure rather than trusting the refs in the set. + +This is a static, repo-only identity bundle. It adds no aggregator runtime and +makes no enforcement, qualification, approval, authority, activation, or external +effect claim. The set stays inactive and fails closed. + ## Inactive duty-separation evaluator `control/v1/evaluate-duty.sh` checks one public core v2 stage tuple against the diff --git a/RESTORE.md b/RESTORE.md index df3dd4b..2764659 100644 --- a/RESTORE.md +++ b/RESTORE.md @@ -330,6 +330,18 @@ The proof validates only the canonical six-section identity bundle. It does not evaluate a policy, grant authority, activate a profile, or enforce sandbox, credential, risk, kill-switch, or evidence behavior. +Restore the two paths in the manifest's inactive Control foundation roll-up block, +then run: + +```sh +bash scripts/test/control-foundation-rollup.test.sh +``` + +This recomputes the six policy and six decision file identities, their common core +generation and package closure, and the inactive fail-closed boundary. It adds no +aggregator runtime and makes no enforcement, qualification, authority, activation, +or external-effect claim. + Restore the five paths in the manifest's inactive duty-separation block, then run: ```sh diff --git a/ci/required-files.txt b/ci/required-files.txt index c7df484..118a0e9 100644 --- a/ci/required-files.txt +++ b/ci/required-files.txt @@ -213,3 +213,7 @@ control/v1/evidence-integrity-decision.json control/v1/evidence-integrity.jq control/v1/evaluate-evidence-integrity.sh scripts/test/control-evidence-integrity.test.sh + +# Inactive Control foundation policy roll-up +control/v1/control-policy-set.json +scripts/test/control-foundation-rollup.test.sh diff --git a/control/v1/control-policy-set.json b/control/v1/control-policy-set.json new file mode 100644 index 0000000..0385b47 --- /dev/null +++ b/control/v1/control-policy-set.json @@ -0,0 +1 @@ +{"body":{"activation_state":"inactive","core_contract":{"generation_id":"g-392d20099dfa99872764009b268c8871914b4dbc0da467ec346baa921818ae3e","package_ref":{"content_id":"core-contract-package.v2","media_type":"application/vnd.ystack.core-contract+json","sha256":"005431c5c7e3a39dc3ab75dfcafd0f09359331667fdcacb140514a4384592716"},"semantic_identity":"core.contracts.v2"},"fail_mode":"closed","policy_version":"v1","sections":[{"decision_ref":{"content_id":"control-decision.credential-policy","media_type":"application/vnd.ystack.control-decision+json","sha256":"006b78ca2a937f77a870f9b5a9a0137c45702c2e6879979d6112025e90176cc0"},"policy_ref":{"content_id":"control-policy.credential-policy","media_type":"application/vnd.ystack.control-policy+json","sha256":"1ca546132d904900e00db532d5f2091336a8dd9f426be591ac7e9d91f47a69f3"},"section_id":"credential-policy"},{"decision_ref":{"content_id":"control-decision.duty-separation","media_type":"application/vnd.ystack.control-decision+json","sha256":"08f8b496a689ab6fefa976de495fa13e1f9d954cd2b33ee44acd3c70022b4697"},"policy_ref":{"content_id":"control-policy.duty-separation","media_type":"application/vnd.ystack.control-policy+json","sha256":"b33a4022c74c8a1ccb06674c080adf2fc106561c426a7d1dd30fdb9865531dbb"},"section_id":"duty-separation"},{"decision_ref":{"content_id":"control-decision.evidence-integrity","media_type":"application/vnd.ystack.control-decision+json","sha256":"2d73d496b1535b6015843ff4c0c250bc77e476532c088ef9d7885ef393849327"},"policy_ref":{"content_id":"control-policy.evidence-integrity","media_type":"application/vnd.ystack.control-policy+json","sha256":"171b89c49c7dd6a58e4c5aa6ca13e8c95d109acf7f67429ecb33fcf1dae7582a"},"section_id":"evidence-integrity"},{"decision_ref":{"content_id":"control-decision.kill-switch","media_type":"application/vnd.ystack.control-decision+json","sha256":"213516a567c9269dcef1085a7fd84a18ab05f61cfc8042be801f1c0725ba27d2"},"policy_ref":{"content_id":"control-policy.kill-switch","media_type":"application/vnd.ystack.control-policy+json","sha256":"60a1171f13fa763076b31c0d65a3a54e70d2af28860e3a6da9ed6bb0038a15f5"},"section_id":"kill-switch"},{"decision_ref":{"content_id":"control-decision.risk-gates","media_type":"application/vnd.ystack.control-decision+json","sha256":"4e7747a495106727a3cda68f8097fae5687425608c3d66d790182e9747359d5b"},"policy_ref":{"content_id":"control-policy.risk-gates","media_type":"application/vnd.ystack.control-policy+json","sha256":"0286be22ec3d3a31be8e2c00c5e57a5b674c3f6b0e6d03c87a670e51e3141bed"},"section_id":"risk-gates"},{"decision_ref":{"content_id":"control-decision.sandbox","media_type":"application/vnd.ystack.control-decision+json","sha256":"c3e89800147d55f7c726ec66c82031915a4220d3eb7867e143f60d7026223bbd"},"policy_ref":{"content_id":"control-policy.sandbox","media_type":"application/vnd.ystack.control-policy+json","sha256":"4afb62e44fd3ad055d157ee23bfcf2917811b9ec05e4923eaa989d95d53c0a5e"},"section_id":"sandbox"}]},"id":"control-policy-set.v1","kind":"control_policy_set","schema_version":1} diff --git a/scripts/test/control-foundation-rollup.test.sh b/scripts/test/control-foundation-rollup.test.sh new file mode 100755 index 0000000..de8329c --- /dev/null +++ b/scripts/test/control-foundation-rollup.test.sh @@ -0,0 +1,272 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2016 +set -euo pipefail +export LC_ALL=C +umask 077 + +root=$(CDPATH='' cd -P -- "${BASH_SOURCE[0]%/*}/../.." && pwd -P) +policy_set="$root/control/v1/control-policy-set.json" +validator="$root/control/v1/validate.sh" +core_wrapper="$root/scripts/core-contract.sh" +core_registry="$root/core/v2/generation-registry.json" +tmp=$(/usr/bin/mktemp -d "${TMPDIR:-/tmp}/ystack-control-rollup-test.XXXXXX") +tmp=$(CDPATH='' cd -P -- "$tmp" && pwd -P) +download='' + +cleanup() { + if [ -n "$download" ] && [ -f "$download" ]; then + /bin/rm -f -- "$download" + fi + /bin/rm -rf -- "$tmp" +} +trap cleanup EXIT +fail() { /usr/bin/printf 'FAIL: %s\n' "$1" >&2; exit 1; } +passes=0 +pass() { passes=$((passes + 1)); /usr/bin/printf 'ok %s - %s\n' "$passes" "$1"; } +sha256_path() { /usr/bin/shasum -a 256 "$1" | /usr/bin/awk '{print $1}'; } +sha256_text() { + /usr/bin/printf '%s' "$1" | /usr/bin/shasum -a 256 | /usr/bin/awk '{print $1}' +} + +platform=$(/usr/bin/uname -s):$(/usr/bin/uname -m) +case "$platform" in + Darwin:*) + jq_asset=jq-osx-amd64 + jq_sha=5c0a0a3ea600f302ee458b30317425dd9632d1ad8882259fcaf4e9b868b2b1ef + ;; + Linux:x86_64) + jq_asset=jq-linux64 + jq_sha=af986793a515d500ab2d35f8d2aecd656e764504b789b66d7e1a0b727a124c44 + ;; + *) fail "unsupported host $platform" ;; +esac +jq_cache_dir="${TMPDIR:-/tmp}/ystack-portable-core-jq16" +/bin/mkdir -p "$jq_cache_dir" +jq_cache="$jq_cache_dir/$jq_asset" +if [ ! -f "$jq_cache" ] || [ "$(sha256_path "$jq_cache")" != "$jq_sha" ]; then + download=$(/usr/bin/mktemp "$jq_cache_dir/.jq-1.6.XXXXXX") + /usr/bin/curl --proto '=https' --tlsv1.2 -fsSL \ + "https://github.com/jqlang/jq/releases/download/jq-1.6/$jq_asset" \ + -o "$download" + [ "$(sha256_path "$download")" = "$jq_sha" ] || fail 'jq release digest' + /bin/chmod 0555 "$download" + /bin/mv "$download" "$jq_cache" + download='' +fi +[ "$(sha256_path "$jq_cache")" = "$jq_sha" ] || fail 'jq digest' +bin="$tmp/bin" +/bin/mkdir -m 0700 "$bin" +/bin/cp "$jq_cache" "$bin/jq" +/bin/chmod 0555 "$bin/jq" +jq_bin="$bin/jq" +[ "$("$jq_bin" --version)" = jq-1.6 ] || fail 'jq identity' + +"$jq_bin" -s -S -c 'if length==1 then .[0] else error("root-count") end' \ + "$policy_set" >"$tmp/canonical.json" || fail 'shipped set parse' +/usr/bin/cmp -s "$policy_set" "$tmp/canonical.json" || fail 'canonical shipped set' +validator_out="$tmp/validator.out" +validator_err="$tmp/validator.err" +PATH="$bin:/usr/bin:/bin" "$validator" validate "$policy_set" \ + >"$validator_out" 2>"$validator_err" || fail 'shipped set validation' +[ ! -s "$validator_out" ] && [ ! -s "$validator_err" ] || fail 'validator output' +pass 'canonical shipped set passes the v1 validator' + +generation=$(/usr/bin/sed -n \ + "s/^PORTABLE_CORE_GENERATION='\(g-[0-9a-f]\{64\}\)'$/\1/p" "$core_wrapper") || + fail 'selected generation' +[[ "$generation" =~ ^g-[0-9a-f]{64}$ ]] || fail 'selected generation shape' +"$jq_bin" -e --arg generation "$generation" ' + [.[] | select(.generation_id==$generation and + .semantic_identity=="core.contracts.v2")] | length==1 +' "$core_registry" >/dev/null || fail 'selected generation registry identity' +generation_sha=$(sha256_text "$generation") + +closure_members="$tmp/core-closure-members.tsv" +closure_paths=( + scripts/core-contract.sh + core/v2/generation-registry.json + "core/v2/generations/$generation/contracts.jq" + "core/v2/generations/$generation/core-ingress.sh" + "core/v2/generations/$generation/modules/profile_graph.jq" + "core/v2/generations/$generation/modules/result_facts.jq" + "core/v2/generations/$generation/modules/result_truth.jq" + "core/v2/generations/$generation/modules/schema.jq" + "core/v2/generations/$generation/modules/stage_request.jq" +) +: >"$closure_members" +for closure_path in "${closure_paths[@]}"; do + /usr/bin/printf '%s\t%s\n' "$closure_path" \ + "$(sha256_path "$root/$closure_path")" >>"$closure_members" +done +closure_descriptor=$("$jq_bin" -Rn -S -c --arg generation_sha "$generation_sha" ' + [inputs | split("\t") | {path:.[0],sha256:.[1]}] as $members | + {schema_version:1,kind:"core_contract_package_closure", + semantic_identity:"core.contracts.v2", + selected_generation_id_sha256:$generation_sha,members:$members} +' <"$closure_members") +core_package_sha=$(sha256_text "$closure_descriptor") +"$jq_bin" -e --arg generation "$generation" --arg package_sha "$core_package_sha" ' + .id=="control-policy-set.v1" and + .body.core_contract=={ + generation_id:$generation, + package_ref:{content_id:"core-contract-package.v2", + media_type:"application/vnd.ystack.core-contract+json",sha256:$package_sha}, + semantic_identity:"core.contracts.v2"} +' "$policy_set" >/dev/null || fail 'shared core contract closure' +pass 'selected core generation and package closure are exact' + +sections=( + credential-policy duty-separation evidence-integrity + kill-switch risk-gates sandbox +) +policy_files=( + credential-policy.json duty-separation-policy.json evidence-integrity-policy.json + kill-switch-policy.json risk-gates-policy.json sandbox-policy.json +) +decision_files=( + credential-policy-decision.json duty-separation-decision.json + evidence-integrity-decision.json kill-switch-decision.json + risk-gates-decision.json sandbox-decision.json +) +policy_media=application/vnd.ystack.control-policy+json +decision_media=application/vnd.ystack.control-decision+json + +check_closure() { + local input=$1 index section policy decision policy_sha decision_sha + PATH="$bin:/usr/bin:/bin" "$validator" validate "$input" >/dev/null 2>&1 || return 1 + "$jq_bin" -e --arg generation "$generation" --arg package_sha "$core_package_sha" ' + .body.core_contract=={ + generation_id:$generation, + package_ref:{content_id:"core-contract-package.v2", + media_type:"application/vnd.ystack.core-contract+json",sha256:$package_sha}, + semantic_identity:"core.contracts.v2"} + ' "$input" >/dev/null || return 1 + for index in 0 1 2 3 4 5; do + section=${sections[$index]} + policy="$root/control/v1/${policy_files[$index]}" + decision="$root/control/v1/${decision_files[$index]}" + policy_sha=$(sha256_path "$policy") + decision_sha=$(sha256_path "$decision") + "$jq_bin" -e --argjson index "$index" --arg section "$section" \ + --arg policy_media "$policy_media" --arg decision_media "$decision_media" \ + --arg policy_sha "$policy_sha" --arg decision_sha "$decision_sha" ' + .body.sections[$index]=={ + decision_ref:{content_id:("control-decision."+$section), + media_type:$decision_media,sha256:$decision_sha}, + policy_ref:{content_id:("control-policy."+$section), + media_type:$policy_media,sha256:$policy_sha},section_id:$section} + ' "$input" >/dev/null || return 1 + done +} + +direct_core_count=0 +for index in 0 1 2 3 4 5; do + section=${sections[$index]} + policy="$root/control/v1/${policy_files[$index]}" + decision="$root/control/v1/${decision_files[$index]}" + policy_sha=$(sha256_path "$policy") + decision_sha=$(sha256_path "$decision") + "$jq_bin" -e --arg section "$section" --arg policy_sha "$policy_sha" ' + .schema_version==1 and .id==("control-decision."+$section) and + .body.activation_state=="inactive" and .body.fail_mode=="closed" and + .body.decision=="allow-observation-only-evaluation" and + .body.policy_ref=={content_id:("control-policy."+$section), + media_type:"application/vnd.ystack.control-policy+json",sha256:$policy_sha} and + .body.semantics.authority_effect=="none" and + (.body.semantics.qualification_effect // "none")=="none" and + (.body.semantics.storage_effect // "none")=="none" and + (.body.semantics.candidate_execution // "none")=="none" and + (.body.semantics.credential_access // "none")=="none" and + (.body.semantics.network_access // "none")=="none" + ' "$decision" >/dev/null || fail "inactive decision boundary $section" + "$jq_bin" -e --arg section "$section" ' + .schema_version==1 and .id==("control-policy."+$section) and + .body.policy_version=="v1" and .body.activation_state=="inactive" and + .body.fail_mode=="closed" and .body.evaluation_mode=="observation-only" + ' "$policy" >/dev/null || fail "inactive policy boundary $section" + if "$jq_bin" -e '.body | has("core_contract")' "$policy" >/dev/null; then + direct_core_count=$((direct_core_count + 1)) + "$jq_bin" -e --arg generation_sha "$generation_sha" \ + --arg package_sha "$core_package_sha" ' + .body.core_contract=={ + generation_id_sha256:$generation_sha, + package_ref:{content_id:"core-contract-package.v2", + media_type:"application/vnd.ystack.core-contract+json",sha256:$package_sha}, + semantic_identity:"core.contracts.v2"} + ' "$policy" >/dev/null || fail "direct core contract $section" + else + case "$section" in + kill-switch|sandbox) ;; + *) fail "missing direct core contract $section" ;; + esac + fi + [ "$policy_sha" = "$("$jq_bin" -er --argjson index "$index" \ + '.body.sections[$index].policy_ref.sha256' "$policy_set")" ] || + fail "policy digest $section" + [ "$decision_sha" = "$("$jq_bin" -er --argjson index "$index" \ + '.body.sections[$index].decision_ref.sha256' "$policy_set")" ] || + fail "decision digest $section" +done +[ "$direct_core_count" -eq 4 ] || fail 'direct core contract count' +check_closure "$policy_set" || fail 'complete shipped closure' +pass 'all twelve refs and six inactive decision boundaries are exact' + +mutate() { + local name=$1 filter=$2 + "$jq_bin" -S -c "$filter" "$policy_set" >"$tmp/$name.json" + /usr/bin/printf '%s\n' "$tmp/$name.json" +} +expect_closure_reject() { + local name=$1 input=$2 out err + out="$tmp/$name.out" + err="$tmp/$name.err" + PATH="$bin:/usr/bin:/bin" "$validator" validate "$input" >"$out" 2>"$err" || + fail "$name validator should accept shape" + [ ! -s "$out" ] && [ ! -s "$err" ] || fail "$name validator output" + if check_closure "$input"; then fail "$name closure accepted"; fi + pass "$name fails the exact shipped closure" +} +expect_validator_reject() { + local name=$1 input=$2 out err status=0 + out="$tmp/$name.out" + err="$tmp/$name.err" + PATH="$bin:/usr/bin:/bin" "$validator" validate "$input" >"$out" 2>"$err" || status=$? + [ "$status" -ne 0 ] && [ ! -s "$out" ] && + [ "$(/bin/cat "$err")" = E_RELATION ] || fail "$name validator closure" + if check_closure "$input"; then fail "$name closure accepted"; fi + pass "$name fails closed in the validator" +} + +for index in 0 1 2 3 4 5; do + for ref_field in policy_ref decision_ref; do + name="ref-$index-$ref_field" + expect_closure_reject "$name" "$(mutate "$name" \ + ".body.sections[$index].$ref_field.sha256=(\"0\"*64)")" + done +done +expect_closure_reject core-generation "$(mutate core-generation \ + '.body.core_contract.generation_id=("g-"+("0"*64))')" +expect_closure_reject core-package "$(mutate core-package \ + '.body.core_contract.package_ref.sha256=("0"*64)')" +expect_closure_reject core-identity "$(mutate core-identity \ + '.body.core_contract.semantic_identity="core.contracts.v3"')" +expect_validator_reject reordered "$(mutate reordered \ + '.body.sections[0:2] |= reverse')" +expect_validator_reject duplicate "$(mutate duplicate \ + '.body.sections[1]=.body.sections[0]')" +expect_validator_reject activation "$(mutate activation \ + '.body.activation_state="active"')" +expect_validator_reject fail-mode "$(mutate fail-mode \ + '.body.fail_mode="open"')" + +for required in control/v1/control-policy-set.json \ + scripts/test/control-foundation-rollup.test.sh; do + [ "$(/usr/bin/grep -Fxc "$required" "$root/ci/required-files.txt")" -eq 1 ] || + fail "manifest $required" +done +/usr/bin/grep -Fq 'Inactive Control foundation roll-up' "$root/README.md" || + fail 'README docs' +/usr/bin/grep -Fq 'control-foundation-rollup.test.sh' "$root/RESTORE.md" || + fail 'RESTORE docs' +pass 'restore manifest and docs' +/usr/bin/printf 'control foundation roll-up: %s focused checks passed\n' "$passes" From ebc8d215dc480fb8da648e7aac7db3ef728fbaab Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 2 Sep 2026 06:05:07 -0400 Subject: [PATCH 2/2] Allow canonical roll-up core identity --- scripts/test/portable-core-schema.test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/test/portable-core-schema.test.sh b/scripts/test/portable-core-schema.test.sh index 88373a8..00e6c74 100755 --- a/scripts/test/portable-core-schema.test.sh +++ b/scripts/test/portable-core-schema.test.sh @@ -759,6 +759,7 @@ v1_activation_path_ok() { v2_activation_path_ok() { case "$1" in README.md|RESTORE.md|ci/required-files.txt|\ + control/v1/control-policy-set.json|\ core/v2/generation-registry.json|\ scripts/core-contract.sh|scripts/lib/profile-resolution.sh|\ scripts/test/portable-core-schema.test.sh|\ @@ -838,6 +839,7 @@ fi schema_v2_expected_live_hits="$schema_test_tmp/v2-expected-live-hits" printf '%s\n' \ ci/required-files.txt \ + control/v1/control-policy-set.json \ core/v2/generation-registry.json \ "core/v2/generations/$schema_v2_generation/core-ingress.sh" \ scripts/core-contract.sh \