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
11 changes: 10 additions & 1 deletion .github/workflows/publish-current-server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ jobs:
python3 source/scripts/test-publisher-safety-guards.py
python3 source/scripts/test-web-console-sbom.py
python3 source/scripts/test-server-backport-consumer-contract.py
python3 source/scripts/test-ai-resource-api-guards.py

declared_release="$(sed -n 's/^ARG SERVER_RELEASE_TAG=//p' \
source/server/Dockerfile.web-compose-release | sort -u)"
Expand Down Expand Up @@ -280,11 +281,17 @@ jobs:
# Only arm cleanup after proving all names are available. A collision
# must never remove another run's container or persistent volumes.
trap cleanup_candidate EXIT
# Exercise the opt-in protocol on this disposable runtime while
# retaining the image's required Java arguments. Image defaults and
# the published configuration remain unchanged.
candidate_java_opts="$(docker image inspect "$CANDIDATE_IMAGE" \
--format '{{range .Config.Env}}{{println .}}{{end}}' | sed -n 's/^JAVA_OPTS=//p')"
docker run -d \
--name "$CANDIDATE_NAME" \
--restart no \
-p 127.0.0.1:18082:8080 \
-e PROXY_PLATFORM_PUBLIC_ORIGIN=https://stack.example.test \
-e "JAVA_OPTS=${candidate_java_opts} -Dapi.ai.bridge.enabled=true -Dapi.ai.service.upgrade.enabled=true -Dapi.ai.resource.change.enabled=true" \
-v "$CATTLE_VOLUME:/var/lib/cattle" \
-v "$MYSQL_VOLUME:/var/lib/mysql" \
-v "$MYSQL_LOG_VOLUME:/var/log/mysql" \
Expand Down Expand Up @@ -422,8 +429,10 @@ jobs:
# Exercise the published API and real auth overlay on this fresh,
# disposable database before the image is allowed to be released.
python3 source/scripts/test-mfa-policy-api.py \
--port 18082 --disposable --registration-basic |
--port 18082 --disposable --registration-basic --ai-resource |
tee "$evidence/mfa-policy-api-smoke.txt"
grep -E '^AI_RESOURCE_API_OK checks=[0-9]+ scoped_create_read=true process_pool=true human_cookie=true no_hosts=true$' \
"$evidence/mfa-policy-api-smoke.txt" >/dev/null
grep -E '^REGISTRATION_BASIC_API_OK checks=7 ordinary_user=true entry_only=true node_active_verified=false script_sha256=[0-9a-f]{64}$' \
"$evidence/mfa-policy-api-smoke.txt" >"$evidence/registration-basic-api-smoke.txt"

Expand Down
11 changes: 11 additions & 0 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Compatibility Contract

## Server v1.6.557 source candidate

This candidate retains Web Console 1.6.199 and bridge 0.2.1 and consumes the
qualified Engine 0.183.357 artifact. It repairs decoded internal Key scope reads
and native process-pool identities. Public typed IDs, live actor ceilings,
child DENY and ordinary Full access are preserved. See the
[candidate note](docs/releases/server-1.6.557.md) for exact component inputs,
unfinished QA and rollback. It is not yet a published or accepted deployment;
the installation examples below remain on the published 556 image.


<a id="server-v16556-typed-resource-flows"></a>

## Server v1.6.556 published
Expand Down
54 changes: 54 additions & 0 deletions docs/releases/server-1.6.557.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Server v1.6.557 candidate

Not published. Install examples continue to use the current published 556 image
until the immutable 557 publication is verified. Deployment acceptance is pending.

## Verified component inputs

- Engine `v0.183.357`, original CI producer `2cd688b0c066658c41e7d3de5bed8c543687f275`,
merged tag source `6a5230ee2c2e6d4c4aa495035b8de30bb5d64d18`, JAR SHA256 `a46e832314b32292e3423133168020acf93052a7dd29e967bbaabc3df6dca8dc`.
Required CodeQL and Security run 40926481583 passed: 373 suites, 3,616 tests,
zero failures/errors and one existing platform-conditional skip.
- Web Console `1.6.199`, producer `80a47df65f6406087b80aa81cfa99fe1f438ecff`,
archive SHA256 `ab60d4baa10685021b74db09b805dee1f866e9bd0fbccea4d54610df907c7570`.
- Optional stdio bridge `0.2.1`, ZIP SHA256
`f1477b74da0caefd7fcef77609b55b7d172683bda4afe3ffb884dfdf5a63dfae`.
Other bundled component pins and live ordinary UI/API paths are unchanged.

## Corrected behavior

Normal validation already decodes resource IDs before internal Key authorization.
The parent resolver now preserves those decoded identities instead of parsing
them a second time. A human-approved create and its original custom-Key receipt
can use the same live scope. Public scope inputs still require exact typed IDs;
foreign IDs, owner ceilings, child DENY and live revision checks remain enforced.

The native process-pool owner supplies a read-only stable bean identity without
changing native display names, null IDs or metrics. AI metadata uses an opaque
identity and a safe fallback display name. Unnamed or duplicate-name pools no
longer break the whole list. The unsupported activeSetting collection is removed
from AI discovery; ordinary settings routes and permissions are unchanged.

The publisher additionally exercises a real scoped-Key preview, human-cookie
confirmation, one empty Stack create and receipt read against its fresh disposable
Server. It checks native pool projection and unsupported-input errors without
Hosts or workloads. This protocol gate does not replace browser acceptance.
No production origin, runtime patch, authentication policy or firewall is changed.

## Acceptance boundary and rollback

Previously unaffected metadata, role and 16 new-modal layout results are retained.
The unfinished 18-pair workflow, remaining read adapters, paired child-DENY control
and weak-model use must be accepted on the designated 8080 installation. This
candidate does not complete every frontend WebMCP workflow. Catalog launch,
advanced forms, other business batches and KVM HOLD remain separately tracked.

Back up the database, preserve named volumes and genuine operator overrides,
and retain the previous image. Keep the three existing opt-in AI properties
and all security, origin and performance settings. No new migration is added.
Before rollback, stop new clients, settle accepted work, cancel unused plans and
revoke temporary Keys. Preserve durable plan receipts and do not replay uncertain
operations. Follow the [Key-policy rollback guide](../upgrades/api-key-policy-rollback.md).
Officially unfixed Medium findings retain their exact package tracking policy;
the release scan must still reject applicable fixed, High/Critical or untracked
findings. Browser SBOM coverage remains explicitly incomplete.
30 changes: 15 additions & 15 deletions scripts/check-server-api-explorer-patch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ python3 scripts/test-web-console-sbom.py
python3 server/security/test_web_console_sbom_scan.py

require_marker "$release_dockerfile" \
'ARG SERVER_RELEASE_TAG=v1.6.556' \
'ARG SERVER_RELEASE_TAG=v1.6.557' \
SERVER_INCREMENTAL_RELEASE_DEFAULT_MISSING
require_marker "$release_dockerfile" \
'org.opencontainers.image.version="${SERVER_RELEASE_TAG}"' \
Expand Down Expand Up @@ -1139,21 +1139,21 @@ require_marker "$build_script" \
'--file server/Dockerfile.web-compose-release' \
SERVER_INCREMENTAL_RELEASE_BUILD_PATH_MISSING
require_marker "$build_script" \
'image=${IMAGE:-pasturestack-validation/server:v1.6.556}' \
'image=${IMAGE:-pasturestack-validation/server:v1.6.557}' \
SERVER_INCREMENTAL_RELEASE_BUILD_VERSION_MISSING
require_marker "$build_script" \
'server_release_tag=${SERVER_RELEASE_TAG:-v1.6.556}' \
'server_release_tag=${SERVER_RELEASE_TAG:-v1.6.557}' \
SERVER_INCREMENTAL_RELEASE_BUILD_DEFAULT_MISSING
require_marker "$build_script" \
'CATTLE_RANCHER_SERVER_VERSION="${server_release_tag}"' \
SERVER_INCREMENTAL_RELEASE_BUILD_RUNTIME_VERSION_MISSING
for release_engine_marker in \
'ARG ORCHESTRATION_ENGINE_RELEASE_TAG=v0.183.356' \
'ARG ORCHESTRATION_ENGINE_RELEASE_TAG=v0.183.357' \
'ARG ORCHESTRATION_ENGINE_ARTIFACT=cattle.jar' \
'ARG ORCHESTRATION_ENGINE_ARTIFACT_SHA256=dfce4c6df625a905a837770780bd09d871752cf9d8493b590145f84a8c78dd09' \
'ARG ORCHESTRATION_ENGINE_COMMIT=24cfa1db113f54de3148cd4ac3191e66a1702806' \
'ARG ORCHESTRATION_ENGINE_ARTIFACT_SHA256=a46e832314b32292e3423133168020acf93052a7dd29e967bbaabc3df6dca8dc' \
'ARG ORCHESTRATION_ENGINE_COMMIT=2cd688b0c066658c41e7d3de5bed8c543687f275' \
'COPY --from=release_artifacts /out/orchestration-engine.jar /tmp/orchestration-engine.jar' \
'ARG ORCHESTRATION_ENGINE_VERSION=0.183.356' \
'ARG ORCHESTRATION_ENGINE_VERSION=0.183.357' \
'sh /usr/share/cattle/verify-engine-archive.sh "${archive}" "${ORCHESTRATION_ENGINE_VERSION}" "${ORCHESTRATION_ENGINE_ARTIFACT_SHA256}"' \
'cattle-resources-${ORCHESTRATION_ENGINE_VERSION}.jar' \
'cattle-app-config-${ORCHESTRATION_ENGINE_VERSION}.jar' \
Expand All @@ -1172,10 +1172,10 @@ for release_engine_marker in \
SERVER_INCREMENTAL_ENGINE_REPLACEMENT_MISSING
done
for release_engine_build_marker in \
'orchestration_engine_release_tag=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.356}' \
'orchestration_engine_release_tag=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.357}' \
'orchestration_engine_artifact=${ORCHESTRATION_ENGINE_ARTIFACT:-cattle.jar}' \
'orchestration_engine_artifact_sha256=${ORCHESTRATION_ENGINE_ARTIFACT_SHA256:-dfce4c6df625a905a837770780bd09d871752cf9d8493b590145f84a8c78dd09}' \
'orchestration_engine_commit=${ORCHESTRATION_ENGINE_COMMIT:-24cfa1db113f54de3148cd4ac3191e66a1702806}' \
'orchestration_engine_artifact_sha256=${ORCHESTRATION_ENGINE_ARTIFACT_SHA256:-a46e832314b32292e3423133168020acf93052a7dd29e967bbaabc3df6dca8dc}' \
'orchestration_engine_commit=${ORCHESTRATION_ENGINE_COMMIT:-2cd688b0c066658c41e7d3de5bed8c543687f275}' \
'CATTLE_CATTLE_VERSION="${orchestration_engine_release_tag}"' \
'cattle-resources-${ORCHESTRATION_ENGINE_VERSION}.jar' \
'< "${server_dir}/artifacts/verify-engine-archive.sh"'; do
Expand Down Expand Up @@ -1694,10 +1694,10 @@ fi
if grep -Fxq '## Current source candidate' README.md; then
for candidate_readme_marker in \
'<a id="current-release"></a>' \
'Server `v1.6.556` is a prepared source candidate, not a published image.' \
'Server `v1.6.557` is a prepared source candidate, not a published image.' \
'run them only after its official release' \
'This candidate is not yet available' \
'ghcr.io/pasturestack/server:v1.6.556'; do
'ghcr.io/pasturestack/server:v1.6.557'; do
require_marker README.md "$candidate_readme_marker" \
SERVER_CURRENT_CANDIDATE_README_MISSING
done
Expand Down Expand Up @@ -2303,7 +2303,7 @@ done
jq -e '
["CVE-2026-107928","CVE-2026-107929","CVE-2026-86409","CVE-2026-86410","CVE-2026-86412","CVE-2026-86414","CVE-2026-87862","CVE-2026-87863","CVE-2026-87864","CVE-2026-87865","CVE-2026-92386","CVE-2026-92387","CVE-2026-92388","CVE-2026-92389","CVE-2026-92390","CVE-2026-92391","CVE-2026-92392","CVE-2026-94266"] as $curl_fixed_cves
| .["@context"] == "https://openvex.dev/ns/v0.2.0"
and .["@id"] == "https://github.com/PastureStack/server/security/openvex/v1.6.556"
and .["@id"] == "https://github.com/PastureStack/server/security/openvex/v1.6.557"
and (.statements | length) == 56
and ([.statements[].vulnerability.name] | length == (unique | length))
and ([.statements[] | select(.status == "fixed") | .vulnerability.name] | sort)
Expand Down Expand Up @@ -2341,7 +2341,7 @@ jq -r '
| @tsv
' "$runtime_vendor_pending" | LC_ALL=C sort -u >"$vendor_pending_fixture"
bash "$vendor_pending_validator" "$runtime_vendor_pending" \
"$vendor_pending_fixture" v1.6.556 >/dev/null
"$vendor_pending_fixture" v1.6.557 >/dev/null
bash scripts/test-vendor-pending-findings.sh >/dev/null
rm -f "$vendor_pending_fixture"
trap - EXIT
Expand Down Expand Up @@ -2430,4 +2430,4 @@ for release_readback_contract in \
fi
done

printf 'SERVER_API_EXPLORER_PATCH_OK release=v1.6.556 base=v1.6.460 engine=0.183.356 web_console=1.6.199 catalog_service=0.20.15 webhook_automation_service=0.10.5 authentication_service=0.4.46 curl=8.18.0-1ubuntu2.8 freemarker=2.3.35 artifact_scan=required vendor_pending=exact-set role_matrix=qa-required locale_layout=qa-required\n'
printf 'SERVER_API_EXPLORER_PATCH_OK release=v1.6.557 base=v1.6.460 engine=0.183.357 web_console=1.6.199 catalog_service=0.20.15 webhook_automation_service=0.10.5 authentication_service=0.4.46 curl=8.18.0-1ubuntu2.8 freemarker=2.3.35 artifact_scan=required vendor_pending=exact-set role_matrix=qa-required locale_layout=qa-required\n'
29 changes: 29 additions & 0 deletions scripts/test-ai-resource-api-guards.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Only guard regressions; actual protocol acceptance requires a real JVM."""
import importlib.util
from pathlib import Path
import unittest
from unittest.mock import patch

spec = importlib.util.spec_from_file_location('ai_smoke', Path(__file__).with_name('test-ai-resource-api.py'))
smoke = importlib.util.module_from_spec(spec)
spec.loader.exec_module(smoke)


class DisposableGuard(unittest.TestCase):
def test_missing_disposable_or_production_ports_fail_before_any_connection(self):
with patch.object(smoke.http.client, 'HTTPConnection', side_effect=AssertionError('Unexpected network request')) as connection:
for port, disposable in [(18082, False), (80, True), (443, True), (8080, True), (0, True), (65536, True)]:
with self.subTest(port=port, disposable=disposable), self.assertRaises(AssertionError):
smoke.run(port=port, administrator_jwt='guard-fixture', administrator_account_id='1a1', disposable=disposable)
connection.assert_not_called()

def test_absent_administrator_session_fails_before_any_connection(self):
with patch.object(smoke.http.client, 'HTTPConnection', side_effect=AssertionError('Unexpected network request')) as connection:
for token, account in [(None, '1a1'), ('', '1a1'), ('guard-fixture', None)]:
with self.subTest(account=account), self.assertRaises(AssertionError):
smoke.run(port=18082, administrator_jwt=token, administrator_account_id=account, disposable=True)
connection.assert_not_called()


if __name__ == '__main__':
unittest.main()
Loading