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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ pr-stack-integrity-evidence.json
/.r0-evidence*/

/keys/*

# Umbrella subchart packaging is derived from the in-repo sources at render time.
apps/_infra/deploy-k8s/Chart.lock
apps/_infra/deploy-k8s/charts/
48 changes: 0 additions & 48 deletions apps/_infra/deploy-k8s/Chart.lock

This file was deleted.

34 changes: 19 additions & 15 deletions apps/_infra/deploy-k8s/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,57 @@ keywords:
maintainers:
- name: OpenCrane
dependencies:
# Every dependency is an in-repo file:// chart, so the checked-out commit is the only
# version authority. The open constraint always accepts the chart each app currently
# declares; helm dependency update packages the sources fresh at render time, and no
# lock file or vendored archive needs a matching bump.
# Shared named-template library (labels, fullname, RBAC rule sets, env helpers).
- name: k8s-platform
version: 0.9.2
version: ">=0.0.0-0"
repository: "file://platform"
# App-owned workload units export named templates; this umbrella only includes them
# with the unchanged parent release context.
- name: opencrane-server
version: 0.9.2
version: ">=0.0.0-0"
repository: "file://../../opencrane/helm"
- name: opencrane-ui
version: 0.9.2
version: ">=0.0.0-0"
repository: "file://../../opencrane-ui/helm"
- name: opencrane-channel-proxy
version: 0.9.2
version: ">=0.0.0-0"
repository: "file://../../channel-proxy/helm"
- name: opencrane-memory-gateway
version: 0.9.2
version: ">=0.0.0-0"
repository: "file://../../memory-gateway/helm"
- name: opencrane-artifact-service
version: 0.8.0
version: ">=0.0.0-0"
repository: "file://../../artifact-service/helm"
- name: opencrane-artifact-preprocessor
version: 0.9.2
version: ">=0.0.0-0"
repository: "file://../../artifact-preprocessor/helm"
- name: opencrane-artifact-scanner
version: 0.9.2
version: ">=0.0.0-0"
repository: "file://../../artifact-scanner/helm"
- name: opencrane-agent-controller
version: 0.9.2
version: ">=0.0.0-0"
repository: "file://../../agent-controller/helm"
- name: opencrane-managed-agent-runtime
alias: managedAgentRuntimePlane
version: 0.8.0
version: ">=0.0.0-0"
repository: "file://../../managed-agent-runtime/helm"
condition: agentController.enabled
- name: opencrane-skill-authoring
version: 0.8.0
version: ">=0.0.0-0"
repository: "file://../../skill-authoring/helm"
- name: opencrane-tool-runner
version: 0.8.0
version: ">=0.0.0-0"
repository: "file://../../tool-runner/helm"
- name: opencrane-cognee
version: 0.8.1
version: ">=0.0.0-0"
repository: "file://../cognee/helm"
- name: opencrane-litellm
version: 0.8.0
version: ">=0.0.0-0"
repository: "file://../litellm/helm"
- name: opencrane-obot
version: 0.8.0
version: ">=0.0.0-0"
repository: "file://../obot/helm"
Binary file removed apps/_infra/deploy-k8s/charts/k8s-platform-0.9.2.tgz
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed apps/_infra/deploy-k8s/charts/opencrane-ui-0.9.2.tgz
Binary file not shown.
17 changes: 15 additions & 2 deletions apps/_infra/deploy-k8s/platform/current-chart-sources.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ current_chart_sources_root()
cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd
}

# Packages the umbrella's subcharts into the checkout itself, for the few contracts that render
# the repository chart directory rather than the disposable fixture. `charts/` and `Chart.lock`
# are derived artifacts (both gitignored), so writing them here is the intended model rather
# than churn. Repeat calls are cheap: Helm reuses archives whose version already matches.
ensure_umbrella_chart_dependencies()
{
local umbrella
umbrella="$(current_chart_sources_root)/apps/_infra/deploy-k8s"
helm dependency update --skip-refresh "$umbrella" >/dev/null
}

prepare_current_chart_sources()
{
if [[ -n "$_CURRENT_CHART_SOURCES_FIXTURE" ]]; then
Expand Down Expand Up @@ -44,7 +55,9 @@ prepare_current_chart_sources()

# Keep the dependency layout identical to Chart.yaml while linking every
# app-owned source directory. New local dependencies then need no fixture
# maintenance and Helm still verifies the committed Chart.lock digest.
# maintenance. Every dependency is an in-repo file:// chart, so the checked-out
# commit is the reproducibility authority and the update packages the current
# sources fresh β€” no lock file or vendored archive to keep in step.
for app_dir in "$root/apps"/*; do
[[ "$(basename "$app_dir")" == "_infra" ]] && continue
ln -s "$app_dir" "$fixture_apps/$(basename "$app_dir")"
Expand All @@ -54,7 +67,7 @@ prepare_current_chart_sources()
ln -s "$infra_dir" "$fixture_apps/_infra/$(basename "$infra_dir")"
done

helm dependency build --skip-refresh "$_CURRENT_CHART_SOURCES_DIR" >/dev/null
helm dependency update --skip-refresh "$_CURRENT_CHART_SOURCES_DIR" >/dev/null
); then
cleanup_current_chart_sources
return 1
Expand Down
15 changes: 8 additions & 7 deletions apps/_infra/deploy-k8s/platform/k8s-deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ fi
POSTGRES_BASELINE_PUBLISHER="$SCRIPT_DIR/../../../postgres/scripts/publish-initdb-baseline-config-map.sh"
POSTGRES_MIGRATION_PUBLISHER="$SCRIPT_DIR/../../../postgres/scripts/publish-database-migration-config-map.sh"
DATABASE_TRANSITION_RESOLVER="$SCRIPT_DIR/../../../../scripts/release-versioning/database-transition.mjs"
DATABASE_SCHEMA_LINEAGE_RESOLVER="$SCRIPT_DIR/../../../../scripts/release-versioning/schema-lineage.mjs"
POSTGRES_MIGRATION_BACKUP="$SCRIPT_DIR/../../../postgres/scripts/create-pre-migration-backup.sh"
POSTGRES_BASELINE_FILE="$SCRIPT_DIR/../../../opencrane/prisma/bootstrap/target-baseline.sql"
REPOSITORY_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
Expand Down Expand Up @@ -315,13 +316,13 @@ validate_unbacked_database_migration_override
DATABASE_TARGET_SCHEMA_VERSION="$(jq -r '.targetSchemaVersion' <<<"$DATABASE_RELEASE_TRANSITION")"
DATABASE_TARGET_BASELINE_SHA256="$(jq -r '.targetBaselineSha256' <<<"$DATABASE_RELEASE_TRANSITION")"
DATABASE_CONVERGENCE_MIGRATION="$(jq '.migration' <<<"$DATABASE_RELEASE_TRANSITION")"
# A repair retry can name the current release while its database still needs the carried migration evidence.
if [[ "$DATABASE_CONVERGENCE_MIGRATION" == "null" ]]; then
previous_release_version="$(jq -r '.database.carriedForwardFromRepositoryVersion // .previousRepositoryVersion // empty' \
"$REPOSITORY_ROOT/releases/$RELEASE_VERSION.json")"
if [[ -n "$previous_release_version" ]]; then
DATABASE_CONVERGENCE_MIGRATION="$(node "$DATABASE_TRANSITION_RESOLVER" "$REPOSITORY_ROOT" "$RELEASE_VERSION" "$previous_release_version" | jq '.migration')"
fi
# A release that changes no schema still meets databases that reached this schema through a real
# migration, and privilege reconciliation compares them against exactly that recorded transition.
# Walk the release chain for the migration that produced this schema. The previous form re-resolved
# the transition against this release's own previous version β€” another same-schema hop that always
# returned null again β€” so every same-schema patch failed the convergence gate on a migrated silo.
if [[ "$DATABASE_TRANSITION_KIND" == "current" && "$DATABASE_CONVERGENCE_MIGRATION" == "null" ]]; then
DATABASE_CONVERGENCE_MIGRATION="$(node "$DATABASE_SCHEMA_LINEAGE_RESOLVER" "$REPOSITORY_ROOT" "$RELEASE_VERSION")" || exit $?
fi
kubectl cluster-info >/dev/null 2>&1 || { err "kubectl can't reach a cluster. Point your context at the target cluster first."; exit 1; }
KUBERNETES_CONTEXT="$(kubectl config current-context 2>/dev/null || true)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ helm()
prepare_current_chart_sources
prepared_chart="$(current_chart_sources_dir)"
[[ "$prepared_chart" != "$AMBIENT_DIR" ]]
grep -Fq 'dependency build --skip-refresh' "$CALLS"
grep -Fq 'dependency update --skip-refresh' "$CALLS"
[[ -L "$prepared_chart/../../opencrane" ]]
cleanup_current_chart_sources
[[ -f "$AMBIENT_FIXTURE/must-survive" ]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,14 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../.." && pwd)"
DEPLOY_SCRIPT="$ROOT_DIR/apps/_infra/deploy-k8s/platform/k8s-deploy.sh"
VERIFY_SCRIPT="$ROOT_DIR/apps/_infra/deploy-k8s/platform/post-deploy-verify.sh"
CHART_DIR="$ROOT_DIR/apps/_infra/deploy-k8s"
CHART_FIXTURE="$(mktemp -d)"
trap 'rm -rf "$CHART_FIXTURE"' EXIT

grep -Fq 'source "$POST_DEPLOY_VERIFY"' "$DEPLOY_SCRIPT"
source "$VERIFY_SCRIPT"

# Render against the current app-owned server chart, not the potentially stale committed archive.
cp -R "$CHART_DIR/." "$CHART_FIXTURE"
helm package "$ROOT_DIR/apps/opencrane/helm" --destination "$CHART_FIXTURE/charts" >/dev/null
rm -f "$CHART_FIXTURE/charts/opencrane-ui-"*.tgz
helm package "$ROOT_DIR/apps/opencrane-ui/helm" --destination "$CHART_FIXTURE/charts" >/dev/null
helm package "$ROOT_DIR/apps/channel-proxy/helm" --destination "$CHART_FIXTURE/charts" >/dev/null
helm package "$ROOT_DIR/apps/memory-gateway/helm" --destination "$CHART_FIXTURE/charts" >/dev/null
# The shared fixture packages every current app-owned chart source, the same way deploy.sh does.
source "$ROOT_DIR/apps/_infra/deploy-k8s/platform/current-chart-sources.sh"
prepare_current_chart_sources
trap 'cleanup_current_chart_sources' EXIT
CHART_FIXTURE="$(current_chart_sources_dir)"
rendered_ingress="$(helm template opencrane-silo "$CHART_FIXTURE" \
--set-string 'memoryGateway.kubernetesApiServerCidrs[0]=10.43.0.1/32' \
--set-string 'memoryGateway.kubernetesApiServerEndpointCidrs[0]=172.18.0.2/32' \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ DEPLOY_CORE="$ROOT_DIR/apps/_infra/deploy-k8s/platform/k8s-deploy.sh"
FINALIZATION="$ROOT_DIR/apps/_infra/deploy-k8s/platform/database-release-finalization.sh"

source "$POLICY"
source "$ROOT_DIR/apps/_infra/deploy-k8s/platform/current-chart-sources.sh"
ensure_umbrella_chart_dependencies

IMAGE_TAG="sha-f7d6771a4a5a075d424c7678d6165dd71c06b522"
CP_TAG="sha-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ DEVELOP_SMOKE="$ROOT_DIR/apps/_infra/deploy-k8s/platform/tests/develop-smoke.sh"
MODEL_HELPER="$ROOT_DIR/apps/_infra/deploy-k8s/platform/initial-model-provider.sh"
COGNEE_POLICY="$ROOT_DIR/apps/_infra/cognee/deploy/image-policy.sh"

source "$ROOT_DIR/apps/_infra/deploy-k8s/platform/current-chart-sources.sh"
ensure_umbrella_chart_dependencies

grep -Fq -- '--acme-email' "$DEPLOY_SCRIPT"
grep -Fq -- '--first-user-email' "$DEPLOY_SCRIPT"
grep -Fq -- '--initial-model-provider' "$DEPLOY_SCRIPT"
Expand Down
34 changes: 34 additions & 0 deletions apps/opencrane/prisma/migrations/tests/verify-postgres.sh
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,40 @@ for database in migrated fresh; do
done
diff --unified "$WORK_DIR/fresh-schema.sql" "$WORK_DIR/migrated-schema.sql"

# A release that changes no schema still ships to silos in both shapes: bootstrapped fresh at the current
# baseline, and migrated into that schema by an earlier release. The deploy engine hands the classifier the
# owning release's evidence from the lineage resolver instead of a migration it must run, so every shape a
# silo can be in has to stay deployable rather than read as incompatible.
CURRENT_RELEASE_VERSION="$(jq -r '.version' "$ROOT/package.json")"
CURRENT_RELEASE_MANIFEST="$ROOT/releases/$CURRENT_RELEASE_VERSION.json"
SCHEMA_LINEAGE="$(node "$ROOT/scripts/release-versioning/schema-lineage.mjs" "$ROOT" "$CURRENT_RELEASE_VERSION")"
POSTGRES_BASELINE_SHA256="$(jq -r '.database.baselineSha256' "$CURRENT_RELEASE_MANIFEST")"
DATABASE_TARGET_BASELINE_SHA256="$POSTGRES_BASELINE_SHA256"
DATABASE_TARGET_SCHEMA_VERSION="$(jq -r '.database.schemaVersion' "$CURRENT_RELEASE_MANIFEST")"
DATABASE_PREVIOUS_MIGRATION_ID="$(jq -r '.id' <<<"$SCHEMA_LINEAGE")"
DATABASE_PREVIOUS_SCHEMA_VERSION="$(jq -r '.fromSchemaVersion' <<<"$SCHEMA_LINEAGE")"
DATABASE_PREVIOUS_TARGET_BASELINE_SHA256="$(jq -r '.sourceTargetBaselineSha256' <<<"$SCHEMA_LINEAGE")"
DATABASE_PREVIOUS_PROTECTED_BASELINE_SHA256S_JSON="$(jq -c '.sourceProtectedBaselineSha256s' <<<"$SCHEMA_LINEAGE")"
DATABASE_PREVIOUS_FRESH_PROTECTED_BASELINE_SHA256="$(jq -r '.freshSourceProtectedBaselineSha256' <<<"$SCHEMA_LINEAGE")"
DATABASE_SOURCE_HISTORY_LINEAGES_JSON="$(jq -c '.sourceHistoryLineages' <<<"$SCHEMA_LINEAGE")"
DATABASE_PREVIOUS_MIGRATION_SQL_SHA256="$(jq -r '.sqlSha256' <<<"$SCHEMA_LINEAGE")"
[[ "$DATABASE_TARGET_SCHEMA_VERSION" == "$(jq -r '.toSchemaVersion' <<<"$SCHEMA_LINEAGE")" ]]

# A fresh bootstrap records where the database was born and writes no migration history. The postgres
# chart's bootstrap Job owns this row in a real silo.
psql_command fresh <<SQL >/dev/null
CREATE SCHEMA "opencrane_bootstrap";
CREATE TABLE "opencrane_bootstrap"."target_baseline" (
"singleton" BOOLEAN PRIMARY KEY DEFAULT TRUE CHECK ("singleton"),
"baseline_sha256" TEXT NOT NULL CHECK ("baseline_sha256" ~ '^[0-9a-f]{64}$')
);
INSERT INTO "opencrane_bootstrap"."target_baseline" ("singleton", "baseline_sha256")
VALUES (TRUE, '$POSTGRES_BASELINE_SHA256');
SQL
assert_classifier_state fresh "current|$POSTGRES_BASELINE_SHA256"
assert_classifier_state fresh_source "completed|$FRESH_PROTECTED_DIGEST"
assert_classifier_state migrated "completed|$PROTECTED_DIGEST"

psql_command migrated --tuples-only --no-align --command \
'SELECT count(*) FROM "persona_questions" WHERE "question_set_id" = '\''personal-agent-onboarding'\'' AND "question_set_version" = 1;' \
| grep -qx '10'
Expand Down
9 changes: 6 additions & 3 deletions docs/agents/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,12 @@ introduced chart has no predecessor and therefore no transition. A migration con
shape once and emits only the current shape; do not retain compatibility aliases in templates or
values.

The umbrella chart pins each local dependency to its owning chart version. Regenerate and review
`Chart.lock` and packaged dependencies after a chart stamp changes. PostgreSQL's chart version tracks
the OpenCrane wrapper; its `appVersion` remains the pinned PostgreSQL engine major.
The umbrella chart declares each local dependency with an open version constraint: every
dependency is an in-repo `file://` chart, so the checked-out commit is the version authority and
`helm dependency update` packages the sources fresh at render time. There is no `Chart.lock` or
vendored archive to keep in step β€” a chart stamp needs no umbrella edit at all. The release gate
still fails when a chart-bearing application is not declared in the umbrella. PostgreSQL's chart
version tracks the OpenCrane wrapper; its `appVersion` remains the pinned PostgreSQL engine major.

## Database migrations

Expand Down
16 changes: 9 additions & 7 deletions docs/ci-and-deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ apply` against a live cluster:
```mermaid
flowchart TD
A[apps/_infra/deploy-k8s/deploy.sh\nsilo profile: flags, presets] --> B[platform/k8s-deploy.sh\nthe install engine]
B --> C[current-chart-sources.sh\nhelm dependency build\nfrom Chart.lock]
B --> C[current-chart-sources.sh\npackages the in-repo\nsubchart sources]
B --> D[database-migration-orchestrator.sh\nCNPG cluster, databases,\nmigration + privileges Jobs]
B --> E[umbrella helm upgrade\nall app subcharts]
E --> F[database-release-finalization.sh\ncredential-checksum roll,\nrollout waits, cert wait]
Expand Down Expand Up @@ -140,10 +140,12 @@ flowchart TD

- **CI green first.** Confirm the `docker.yml` run for the exact SHA is green before deploying;
the deploy scripts pull published images and never build them.
- **`helm dependency build`, never `dependency update`, on the deploy path.** The engine resolves
subcharts from `Chart.lock` for reproducibility. `dependency update` re-resolves and can drift.
(Regenerating the lock/archives after a chart stamp is the one place `dependency update` is
correct β€” review the diff.)
- **Subchart packaging is derived, never committed.** Every umbrella dependency is an in-repo
`file://` chart, so the checked-out commit is the version authority: the deploy fixture runs
`helm dependency update --skip-refresh` and packages the current sources. There is no
`Chart.lock` or vendored archive to regenerate, and a chart version bump needs no umbrella
edit. (The bootstrap prerequisites are the opposite case: external charts stay pinned by
version and digest.)
- **A green `helm template`/CI render does not prove a live `helm upgrade` works.** Stateful
services need their PVC semantics, reconcile-retry, and Secret-change pod-roll trigger checked
before deploying β€” see the live-upgrade checklist in the deploy ledger.
Expand Down Expand Up @@ -177,8 +179,8 @@ summary.
`appVersion` where a chart exists. Version-only mirror edits are "stamp-only" and do not count
as changes themselves.
- A **changed chart** bumps its chart version to the root version and adds exactly one
`helm/migrations/<from>-to-<to>.json` transition; the umbrella's `Chart.lock` and packaged
archives are then regenerated and reviewed.
`helm/migrations/<from>-to-<to>.json` transition. The umbrella needs no edit: it declares its
in-repo dependencies with open constraints and packages them fresh at render time.
- A **database schema change** updates the clean target baseline and adds one adjacent, reviewed
SQL transition under `apps/opencrane/prisma/migrations/<from>-to-<to>/`, bound by digest.
- Adjacent minor trains (`0.8.x β†’ 0.9.0`) are the only automatic transition. Patch, skipped-minor,
Expand Down
Loading
Loading