@serve.zone/appstore 33.11.0 → 33.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +14 -0
- package/dist_catalog/catalog.digest +1 -1
- package/dist_catalog/catalog.envelope.json +210 -6
- package/dist_catalog/catalog.json +208 -4
- package/dist_ts_client/storage.d.ts +2 -1
- package/dist_ts_client/storage.js +17 -4
- package/dist_ts_client/types.d.ts +11 -0
- package/package.json +2 -2
- package/readme.md +29 -4
- package/ts_client/readme.md +1 -1
- package/ts_client/storage.ts +17 -2
- package/ts_client/types.ts +18 -0
package/changelog.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-10-17 - 33.12.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- publish the Cloudly 1.30.0 template for Cloudly 33.21.0, which declares the runtime log ledger database backup exclusions (cloudly)
|
|
8
|
+
- Add Cloudly app version 1.30.0 on 1.29.4's config with `databaseBackupExclusions` for the `mongodb` collections `cloudly_runtime_log_attempts`, `cloudly_runtime_log_batches`, `cloudly_runtime_log_captures`, `cloudly_runtime_log_inline_payloads`, `cloudly_runtime_log_node_discards` and `cloudly_runtime_log_queue` and `requiresFeatures: ['storage.database.backup-exclusions', 'storage.object-storage.backup-exclusions']`; a host without the database feature lists it, refuses it and keeps 1.29.4 as its newest Cloudly target.
|
|
9
|
+
- It is the upgrade target of 1.29.4 down to 1.28.3; Cloudly 33.21.0 adds no data step (data version 0.31.0) and re-records the running log attempts on every start, so a restore that brings the ledger back empty keeps the logs of every running workload. The upgrade flags stay set because hosts read them from the target alone.
|
|
10
|
+
- carry `databaseBackupExclusions` through the storage handoff (storage)
|
|
11
|
+
- `normalizeAppStoreStorageConfig` returns `databaseBackupExclusions` by database (`mongodb`), empty when a version declares none, derives `storage.database.backup-exclusions` into `requiredFeatureIds` in `requiresFeatures` order and fails closed on a target that does not advertise it.
|
|
12
|
+
|
|
13
|
+
### Maintenance
|
|
14
|
+
|
|
15
|
+
- `@serve.zone/interfaces` 32.56.0 -> 32.58.0 for `IAppStoreVersionConfig.databaseBackupExclusions` and the feature `storage.database.backup-exclusions`.
|
|
16
|
+
|
|
3
17
|
## 2026-10-16 - 33.11.0
|
|
4
18
|
|
|
5
19
|
### Features
|
|
@@ -1 +1 @@
|
|
|
1
|
-
sha256:
|
|
1
|
+
sha256:85f53ee1fbb430c24ca61951fab760815ec6a52d633a9df0fc1ee790a07e017b
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
"major": 1,
|
|
6
6
|
"minor": 0
|
|
7
7
|
},
|
|
8
|
-
"revision":
|
|
9
|
-
"publishedAt": "2026-10-
|
|
10
|
-
"sourceRevision": "
|
|
8
|
+
"revision": 33000012000000,
|
|
9
|
+
"publishedAt": "2026-10-17T07:18:39.000Z",
|
|
10
|
+
"sourceRevision": "12931b30771f0258982cac18513be47a8793547f",
|
|
11
11
|
"apps": [
|
|
12
12
|
{
|
|
13
13
|
"info": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"Docs": "https://serve.zone"
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
|
-
"latestVersion": "1.
|
|
63
|
+
"latestVersion": "1.30.0",
|
|
64
64
|
"versions": [
|
|
65
65
|
{
|
|
66
66
|
"version": "1.0.0",
|
|
@@ -8865,6 +8865,210 @@
|
|
|
8865
8865
|
}
|
|
8866
8866
|
},
|
|
8867
8867
|
"configDigest": "sha256:5379de0dd94d6bddcb2541c3e95f70f6868619ab62bfd6e117b5bf7e11e58a2c"
|
|
8868
|
+
},
|
|
8869
|
+
{
|
|
8870
|
+
"version": "1.30.0",
|
|
8871
|
+
"config": {
|
|
8872
|
+
"image": "code.foss.global/serve.zone/cloudly@sha256:cd0bfc82575d9a4a9d0808e19ec61b0bf188d83729d66195afbf5f373164f16e",
|
|
8873
|
+
"targetPorts": [
|
|
8874
|
+
{
|
|
8875
|
+
"name": "web",
|
|
8876
|
+
"port": 3000,
|
|
8877
|
+
"protocol": "http",
|
|
8878
|
+
"default": true
|
|
8879
|
+
}
|
|
8880
|
+
],
|
|
8881
|
+
"envVars": [
|
|
8882
|
+
{
|
|
8883
|
+
"key": "SERVEZONE_ENVIRONMENT",
|
|
8884
|
+
"value": "production",
|
|
8885
|
+
"description": "Cloudly runtime environment.",
|
|
8886
|
+
"required": true
|
|
8887
|
+
},
|
|
8888
|
+
{
|
|
8889
|
+
"key": "SERVEZONE_PUBLIC_ORIGIN",
|
|
8890
|
+
"value": "https://${SERVICE_DOMAIN}",
|
|
8891
|
+
"description": "Canonical public Cloudly origin including protocol and optional public port.",
|
|
8892
|
+
"required": true
|
|
8893
|
+
},
|
|
8894
|
+
{
|
|
8895
|
+
"key": "SERVEZONE_LISTENPORT",
|
|
8896
|
+
"value": "3000",
|
|
8897
|
+
"description": "Internal Cloudly HTTP port inside the container.",
|
|
8898
|
+
"required": true
|
|
8899
|
+
},
|
|
8900
|
+
{
|
|
8901
|
+
"key": "SERVEZONE_SSLMODE",
|
|
8902
|
+
"value": "external",
|
|
8903
|
+
"description": "Use external TLS termination through Onebox or dcrouter.",
|
|
8904
|
+
"required": true
|
|
8905
|
+
},
|
|
8906
|
+
{
|
|
8907
|
+
"key": "SERVEZONE_SECRET_KEYRING_PATH",
|
|
8908
|
+
"value": "/run/secrets/cloudly-secrets-v2-keyring.json",
|
|
8909
|
+
"description": "Path to the Cloudly Secrets v2 keyring mounted by Onebox; the keyring value is never placed in the public environment.",
|
|
8910
|
+
"required": true
|
|
8911
|
+
},
|
|
8912
|
+
{
|
|
8913
|
+
"key": "SERVEZONE_SECRET_KEYRING_FILE",
|
|
8914
|
+
"description": "Existing Cloudly Secrets v2 keyring. Onebox stores it encrypted and mounts it only as a root-readable file; retain the existing keyring when upgrading.",
|
|
8915
|
+
"required": true,
|
|
8916
|
+
"secret": true,
|
|
8917
|
+
"delivery": {
|
|
8918
|
+
"type": "file",
|
|
8919
|
+
"targetPath": "/run/secrets/cloudly-secrets-v2-keyring.json",
|
|
8920
|
+
"uid": 0,
|
|
8921
|
+
"gid": 0,
|
|
8922
|
+
"mode": 256
|
|
8923
|
+
}
|
|
8924
|
+
},
|
|
8925
|
+
{
|
|
8926
|
+
"key": "SERVEZONE_WORKLOADINIT_APPROVAL_PATH",
|
|
8927
|
+
"value": "/run/secrets/cloudly-workloadinit-approval.json",
|
|
8928
|
+
"description": "Private WorkloadInit release-approval artifact mounted by Onebox for Coreflow secret-runtime registration.",
|
|
8929
|
+
"required": true
|
|
8930
|
+
},
|
|
8931
|
+
{
|
|
8932
|
+
"key": "SERVEZONE_WORKLOADINIT_APPROVAL_FILE",
|
|
8933
|
+
"description": "Operator-approved WorkloadInit release authority. Onebox stores it encrypted and mounts it only as a root-readable file.",
|
|
8934
|
+
"required": true,
|
|
8935
|
+
"secret": true,
|
|
8936
|
+
"delivery": {
|
|
8937
|
+
"type": "file",
|
|
8938
|
+
"targetPath": "/run/secrets/cloudly-workloadinit-approval.json",
|
|
8939
|
+
"uid": 0,
|
|
8940
|
+
"gid": 0,
|
|
8941
|
+
"mode": 256
|
|
8942
|
+
}
|
|
8943
|
+
},
|
|
8944
|
+
{
|
|
8945
|
+
"key": "MONGODB_URL",
|
|
8946
|
+
"value": "${MONGODB_URI}",
|
|
8947
|
+
"description": "Authenticated MongoDB wire-protocol URL provisioned by Onebox Corestore.",
|
|
8948
|
+
"required": true
|
|
8949
|
+
},
|
|
8950
|
+
{
|
|
8951
|
+
"key": "MONGODB_NAME",
|
|
8952
|
+
"value": "${MONGODB_DATABASE}",
|
|
8953
|
+
"description": "MongoDB wire-protocol database name provisioned by Onebox Corestore.",
|
|
8954
|
+
"required": true
|
|
8955
|
+
},
|
|
8956
|
+
{
|
|
8957
|
+
"key": "MONGODB_USER",
|
|
8958
|
+
"value": "${MONGODB_USERNAME}",
|
|
8959
|
+
"description": "MongoDB wire-protocol username provisioned by Onebox Corestore.",
|
|
8960
|
+
"required": true
|
|
8961
|
+
},
|
|
8962
|
+
{
|
|
8963
|
+
"key": "MONGODB_PASS",
|
|
8964
|
+
"value": "${MONGODB_PASSWORD}",
|
|
8965
|
+
"description": "MongoDB wire-protocol password provisioned by Onebox Corestore.",
|
|
8966
|
+
"required": true
|
|
8967
|
+
},
|
|
8968
|
+
{
|
|
8969
|
+
"key": "S3_ENDPOINT",
|
|
8970
|
+
"value": "onebox-corestore",
|
|
8971
|
+
"description": "S3 endpoint host provisioned by Onebox Corestore.",
|
|
8972
|
+
"required": true
|
|
8973
|
+
},
|
|
8974
|
+
{
|
|
8975
|
+
"key": "S3_PORT",
|
|
8976
|
+
"value": "9000",
|
|
8977
|
+
"description": "S3 endpoint port provisioned by Onebox Corestore.",
|
|
8978
|
+
"required": true
|
|
8979
|
+
},
|
|
8980
|
+
{
|
|
8981
|
+
"key": "S3_USESSL",
|
|
8982
|
+
"value": "false",
|
|
8983
|
+
"description": "Use plain HTTP for internal Corestore S3 traffic on the Onebox network.",
|
|
8984
|
+
"required": true
|
|
8985
|
+
},
|
|
8986
|
+
{
|
|
8987
|
+
"key": "S3_BUCKET",
|
|
8988
|
+
"value": "${S3_BUCKET}",
|
|
8989
|
+
"description": "Cloudly's own S3 bucket, provisioned by Onebox Corestore: registry storage and kept workload logs.",
|
|
8990
|
+
"required": true
|
|
8991
|
+
},
|
|
8992
|
+
{
|
|
8993
|
+
"key": "S3_ACCESSKEY",
|
|
8994
|
+
"value": "${S3_ACCESS_KEY}",
|
|
8995
|
+
"description": "S3 access key provisioned by Onebox Corestore.",
|
|
8996
|
+
"required": true
|
|
8997
|
+
},
|
|
8998
|
+
{
|
|
8999
|
+
"key": "S3_SECRETKEY",
|
|
9000
|
+
"value": "${S3_SECRET_KEY}",
|
|
9001
|
+
"description": "S3 secret key provisioned by Onebox Corestore.",
|
|
9002
|
+
"required": true
|
|
9003
|
+
}
|
|
9004
|
+
],
|
|
9005
|
+
"platformRequirements": {
|
|
9006
|
+
"mongodb": true,
|
|
9007
|
+
"s3": true
|
|
9008
|
+
},
|
|
9009
|
+
"backupExclusions": [
|
|
9010
|
+
{
|
|
9011
|
+
"storageRequest": "legacy-s3",
|
|
9012
|
+
"keyPrefixes": [
|
|
9013
|
+
"runtime-logs/"
|
|
9014
|
+
],
|
|
9015
|
+
"reason": "Workload logs; a restore brings back state, not past logs."
|
|
9016
|
+
}
|
|
9017
|
+
],
|
|
9018
|
+
"databaseBackupExclusions": [
|
|
9019
|
+
{
|
|
9020
|
+
"database": "mongodb",
|
|
9021
|
+
"collections": [
|
|
9022
|
+
"cloudly_runtime_log_attempts",
|
|
9023
|
+
"cloudly_runtime_log_batches",
|
|
9024
|
+
"cloudly_runtime_log_captures",
|
|
9025
|
+
"cloudly_runtime_log_inline_payloads",
|
|
9026
|
+
"cloudly_runtime_log_node_discards",
|
|
9027
|
+
"cloudly_runtime_log_queue"
|
|
9028
|
+
],
|
|
9029
|
+
"reason": "Workload log ledger; a restore brings back state, not past logs."
|
|
9030
|
+
}
|
|
9031
|
+
],
|
|
9032
|
+
"platformOidc": {
|
|
9033
|
+
"redirectPath": "/auth/oidc/callback",
|
|
9034
|
+
"roles": [
|
|
9035
|
+
{
|
|
9036
|
+
"id": "admin",
|
|
9037
|
+
"label": "Administrator",
|
|
9038
|
+
"description": "Full Cloudly administration for this hosted instance."
|
|
9039
|
+
},
|
|
9040
|
+
{
|
|
9041
|
+
"id": "user",
|
|
9042
|
+
"label": "User",
|
|
9043
|
+
"description": "Standard authenticated Cloudly access for this hosted instance."
|
|
9044
|
+
}
|
|
9045
|
+
],
|
|
9046
|
+
"environmentVariables": {
|
|
9047
|
+
"issuerUrl": "SERVEZONE_PLATFORM_OIDC_ISSUER",
|
|
9048
|
+
"clientId": "SERVEZONE_PLATFORM_OIDC_CLIENT_ID",
|
|
9049
|
+
"clientSecret": "SERVEZONE_PLATFORM_OIDC_CLIENT_SECRET",
|
|
9050
|
+
"redirectUri": "SERVEZONE_PLATFORM_OIDC_REDIRECT_URI",
|
|
9051
|
+
"audience": "SERVEZONE_PLATFORM_OIDC_AUDIENCE"
|
|
9052
|
+
},
|
|
9053
|
+
"clientAuthenticationMethod": "client_secret_basic"
|
|
9054
|
+
},
|
|
9055
|
+
"minOneboxVersion": "33.8.0",
|
|
9056
|
+
"changelog": "Move Cloudly to 33.21.0, a feature release over 33.20.4: Cloudly's runtime log ledger is no longer part of its backups, and a restore keeps the logs of every running workload. This template declares the six collections of the ledger, cloudly_runtime_log_attempts, cloudly_runtime_log_batches, cloudly_runtime_log_captures, cloudly_runtime_log_inline_payloads, cloudly_runtime_log_node_discards and cloudly_runtime_log_queue, as databaseBackupExclusions of its MongoDB database: a backup exports them with their indexes and without their documents, so a restore brings them back empty while the slots and the workloads they run stay. The ledger holds most of a long-running Cloudly's database, so its backups shrink to the state they protect. Every Cloudly 33.21.0 start writes the log record of each running attempt that has none, before it serves node requests, so the nodes' next batches are kept again instead of being refused as attempts whose logs Cloudly does not keep; the first batch of each capture after a restore states the numbers the restore lost as a gap. A start whose attempts all have their records writes nothing, and an attempt whose record cannot be written is named in a warning and tried again at the next start. requiresFeatures names storage.database.backup-exclusions beside storage.object-storage.backup-exclusions: a host that does not fulfil the database feature keeps listing this version and refuses it as an install or upgrade target, and its newest Cloudly target stays 1.29.4; a host without the object-storage feature keeps 1.28.4. Like 1.29.4, this template declares Cloudly's workload log objects non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket (storage request legacy-s3). Cloudly 33.21.0 adds no data step and no index and keeps the data version 0.31.0, so an upgrade from 1.29.4, 1.29.3 or 1.29.2 runs no data step; it adds no collection, and the template requires Onebox 33.8.0. From 1.29.4 the upgrade moves Cloudly from 33.20.4 to 33.21.0. The rest of this changelog is that of 1.29.4, the Cloudly 33.20.4 template, and describes the changes of Cloudly 33.20.4 and the releases before it that an upgrade from 1.29.3 or earlier also takes: Move Cloudly to 33.20.4, a fixes-only patch over 33.20.3: the runtime log sweep completes again over a large ledger and no longer keeps other controller transactions retrying. Over a ledger of about 140,000 rows every sweep pass failed after about 126 s with the transaction lifetime exceeded, because two of its reads cost the whole ledger rather than their own work: the walk over dropped rows was sorted by id, so it walked the id index over every row to find the few dropped ones, and the orphan listing's held check asked for either state at once, so it read every row of a capture for each of up to 1,000 listed keys in one transaction. Such a step outlived the database's 60 s transaction lifetime and was retried for 120 s, and since every step fences the controller record, other controller transactions write-conflicted for about two of every three minutes and managed VPN credentials missed their 6 s budget at slot starts. Now the sweep's ledger walks are unsorted, the held check reads one state at a time and stops at the first row, and whether a dropped attempt still has rows is probed instead of counted. Every sweep step is one controller transaction of at most 16 rows or keys (was 64; the held check took up to 1,000 keys in one), bounded by a 15 s deadline that includes its retries and commit, so a step that cannot finish ends the pass at once and the next pass continues after the last committed step; before each step the sweep waits for the critical node requests in flight, as the log drain does. No index is added, so the first start builds none. Cloudly 33.20.4 adds no data step and keeps the data version 0.31.0, so an upgrade from 1.29.3 or 1.29.2 runs no data step; it adds no collection, changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.29.3 and requires Onebox 33.8.0. Like 1.29.3, this template declares Cloudly's workload logs non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket (storage request legacy-s3), and requiresFeatures names storage.object-storage.backup-exclusions; a host that does not fulfil the feature keeps listing this version and refuses it as an install or upgrade target. From 1.29.3 the upgrade moves Cloudly from 33.20.3 to 33.20.4. From 1.29.2 it also takes the changes of Cloudly 33.20.3. From 1.29.1 or earlier it also takes the changes of Cloudly 33.20.2, including the forward-only data step service-reconciliation-observations from 0.30.0 to 0.31.0, which every Cloudly before 33.20.2 refuses, so a return to 1.29.1 or an earlier template means restoring the database backup taken before the upgrade; from 1.29.0 also those of Cloudly 33.20.1; from 1.28.6 or earlier also those of Cloudly 33.20.0, including the first start that creates the collection cloudly_runtime_log_inline_payloads and the log ledger rows Cloudly 33.19.2 and earlier refuse; from 1.28.5 or 1.28.3, which run the Cloudly 33.19.1 image, also those of Cloudly 33.19.2, and from 1.28.4 or earlier the template also gains the declaration. The rest of this changelog is that of 1.29.3, the Cloudly 33.20.3 template, and describes the changes of Cloudly 33.20.3 and the releases before it that an upgrade from 1.29.2 or earlier also takes: Move Cloudly to 33.20.3, a fixes-only patch over 33.20.2: a service that already holds a Corestore binding of a capability can adopt its own legacy resource of that capability into that binding. Services deployed before Corestore 32 hold a canonical Corestore database and bucket binding created by the generic path, and Corestore (since 34.1.4) refuses any binding beside an unauthorized legacy resource of the capability (legacy-adoption-pending). adoptCorestoreLegacyResources refused such a service binding-exists, a tombstone keeps the service's binding slot and a generic upsert cannot write the adoption, so no path could adopt the resource and the service stayed down. Now, when the legacy owner adopts its own resource (no successorServiceId) and holds a binding of the capability that is enabled, not tombstoned, on the canonical Corestore provider and adopts nothing yet, and the binding with the adoption is valid (a bucket binding names the legacy bucket and its retention intent exactly, a database binding carries neither), the adoption is written onto that binding in the adoption's transaction, a compare-and-set on the binding as it was read; its status returns to requested with its reason cleared, the resource's claim is written, and the plan names the binding's id. Every other binding the adopting service holds still refuses binding-exists: another bucket or retention intent, a database binding with a bucket, another provider, a tombstoned or disabled binding, one adopting another resource, and any binding of a named successor. Adopting again answers the marked binding unchanged. Corestore needs no change: its offline migration reads the plan as for a created binding. Cloudly 33.20.3 adds no data step and keeps the data version 0.31.0, so an upgrade from 1.29.2 runs no data step; it adds no collection, changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.29.2 and requires Onebox 33.8.0. Like 1.29.2, this template declares Cloudly's workload logs non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket (storage request legacy-s3), and requiresFeatures names storage.object-storage.backup-exclusions; a host that does not fulfil the feature keeps listing this version and refuses it as an install or upgrade target. From 1.29.2 the upgrade moves Cloudly from 33.20.2 to 33.20.3. From 1.29.1 or earlier it also takes the changes of Cloudly 33.20.2, including the forward-only data step service-reconciliation-observations from 0.30.0 to 0.31.0, which every Cloudly before 33.20.2 refuses, so a return to 1.29.1 or an earlier template means restoring the database backup taken before the upgrade; from 1.29.0 also those of Cloudly 33.20.1; from 1.28.6 or earlier also those of Cloudly 33.20.0, including the first start that creates the collection cloudly_runtime_log_inline_payloads and the log ledger rows Cloudly 33.19.2 and earlier refuse; from 1.28.5 or 1.28.3, which run the Cloudly 33.19.1 image, also those of Cloudly 33.19.2, and from 1.28.4 or earlier the template also gains the declaration. The rest of this changelog is that of 1.29.2, the Cloudly 33.20.2 template, and describes the changes of Cloudly 33.20.2 and the releases before it that an upgrade from 1.29.1 or earlier also takes: Move Cloudly to 33.20.2, which keeps its registry storage bounded, keeps a running replica when a rollout asks for what it already runs, and no longer loses gateway, mail and CoreMail status while a service lifecycle lease is held; its startup runs the forward-only data step to 0.31.0. A storage retention of the registry runs 3 minutes after start, then daily, or hourly while work remains: before, every tag push recorded an image release that protected its manifests and blobs forever, a greenfield cleanup left its repository behind, blobs no manifest referenced were never collected, and the release rows of deleted services kept protecting their images. A release stays while any service's plan names it or its repository root, any service that is not immutable runs a tag naming its root, a deployment operation still needs it (not terminal, or failed retryably within 7 days) or an unfinished rollout or adoption names it; besides, its service keeps its 3 most recently promoted accepted releases (its rollback targets) and every release observed or promoted within 7 days. Every other release is pruned with the tags naming its root and its image versions, at most 200 a pass. Manifests and blobs nothing reaches any more are deleted once they are older than 24 hours and unused for 24 hours, blobs only when every repository could be judged and Cloudly has served for an hour, at most 20,000 objects a pass; a repository that cannot be read is named in the pass's summary line and skipped, and repositories no service maps to are reported and kept. Every deletion runs in holds of the registry mutation lock that take no new item once 2 seconds passed, so the pushes queued meanwhile go between holds. The per-push prune is gone: after every tagged push it read every repository while holding that lock, and it could delete the child manifests of a multi-platform push in flight. A service deletion deletes only what its own repository owns, its manifests and then its tags, without reading any other repository and without deleting a blob, so a broken repository elsewhere no longer stops it; a greenfield cleanup now retires the provisional service's registry state too, while the operation's reservation holds the repository. The registry (@push.rocks/smartregistry 2.13.0) reads its storage strictly, refuses a manifest PUT that names a blob or child manifest it does not hold with 400 MANIFEST_BLOB_UNKNOWN (a client pushes blobs and children first), collects abandoned upload chunks and serves blob byte ranges, so a layer pull can resume. A rollout, or a retry of one, whose image and configuration a replica already runs keeps that replica running instead of replacing it: whether an incumbent runs a desire is judged by its run's identity or by its content (registry host, repository and image digest, platform, config reference, network attachment, secret authority and storage claims). Deliberately, a spec change that moves only placement or network policy (publicEgress, platformEndpointIds, published ports) no longer restarts a replica, because Pallet applies a new network projection to the running workload in place; upgrading Cloudly restarts no replica. Gateway, mail and CoreMail status reported while a service lifecycle lease is held is no longer refused and dropped after 12 retries: reconciliation status is observation and lives beside the service document, one record per observer in the new collection cloudly_service_reconciliation_stages, the newest report winning, and every API answer that carries a service joins it back as data.reconciliationStatus, so the dashboard reads it as before. Cloudly 33.20.2 adds the forward-only data step service-reconciliation-observations from 0.30.0 to 0.31.0, which moves each readable stage of every service's status into that collection and unsets the status in the service document; it changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.29.1 and requires Onebox 33.8.0. The step has no way back: every Cloudly before 33.20.2, 33.20.1 included, refuses to start on data version 0.31.0, so a return to 1.29.1 or an earlier template means restoring the database backup taken before the upgrade, and the registry manifests and blobs the retention deleted by then do not come back with a restored database. Like 1.29.1, this template declares Cloudly's workload logs non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket (storage request legacy-s3), and requiresFeatures names storage.object-storage.backup-exclusions; a host that does not fulfil the feature keeps listing this version and refuses it as an install or upgrade target. From 1.29.1 the upgrade moves Cloudly from 33.20.1 to 33.20.2 and runs the step to 0.31.0. From 1.29.0 it also takes the changes of Cloudly 33.20.1; from 1.28.6 or earlier also those of Cloudly 33.20.0, including the first start that creates the collection cloudly_runtime_log_inline_payloads and the log ledger rows Cloudly 33.19.2 and earlier refuse; from 1.28.5 or 1.28.3, which run the Cloudly 33.19.1 image, also those of Cloudly 33.19.2, and from 1.28.4 or earlier the template also gains the declaration. The rest of this changelog is that of 1.29.1, the Cloudly 33.20.1 template, and describes the changes of Cloudly 33.20.1 and the releases before it that an upgrade from 1.29.0 or earlier also takes: Move Cloudly to 33.20.1, a fixes-only patch over 33.20.0: a Cloudly restart no longer blanks a cluster's ingress routes, a revocation withdraws exactly the routes it revokes, and a Pallet registration Cloudly cannot serve for a reason of its own is refused as momentary. After each cold start of Cloudly 33.20.0 the cluster relay applied two route tables of 0 routes before the full one, and every site of the cluster answered 502 for about 2.5 minutes. Two causes, both fixed. First, Cloudly published an empty revocation as its first table of a cluster, because it held no table of it yet; now the first pull or refresh compiles the plan and issues the full table, and a relay's read that comes before it waits up to 6 s for it and is otherwise answered { table: null }, so the relay keeps the table it holds and the full one follows as a push. Second, every node registers again after a restart, which opens a new session, and a route endpoint required an observation reported under the current session, so every route waited until each node reported anew. A reporter is now one node instance under one credential generation (nodeInstanceNonce, Pallet's per-process nonce, stored on every session record): a node instance that only registered again keeps its observations, its cutover uplink endpoint and its cluster ingress registration authority until it reports anew, while a new Pallet process, a new credential generation or a deleted node supersedes them at once. A route's identity names the node instance and credential generation instead of the session id and epoch, so such a registration changes no route. Route sources that cannot be read right after a start keep the relay's table for at most 180 s, logged once per cluster; then one line names the clusters still waiting and their routes are withdrawn as before. A revocation is now the held table less the revoked routes and port routes, issued and pushed at once: before, any revoked route identity made Cloudly publish an empty table for the whole cluster before it fetched certificates for the next one, so one node registering again or one changed route blanked every site of the cluster. A revoked ingress designation still withdraws everything, and route sources that cannot be read still withdraw every route. A Pallet registration that fails for a reason of Cloudly's own (a database connection lost, a write conflict, a race with a credential being applied, a controller not ready yet or any unnamed failure) is answered controller-unavailable and a spent budget controller-deadline, the @serve.zone/interfaces 32.57.0 runtime session refusal contract, so the node waits and registers again instead of ending its session; before, only a spent budget or a stopping controller carried the refusal payload, and the node read every other failure as a verdict. Only the named verdicts carry no payload: an inactive credential, a deleted, retiring (node-retiring) or foreign node, an unverifiable carrier, a replay that states another registration (registration-replay-mismatch), a moved controller identity, an invalid body. A credential check whose store cannot be read now fails instead of reading as a credential that does not verify, so a registration whose carrier Cloudly could not check is momentary too. A request is answered at its budget even when its work ignores the abort signal, and each registration refusal is logged once per node and reason, value-free, until the node's next accepted registration. Cloudly 33.20.1 adds no data step and keeps the data version 0.30.0, so an upgrade from 1.29.0 runs no data step; it adds no collection, changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.29.0 and requires Onebox 33.8.0. Like 1.29.0, this template declares Cloudly's workload logs non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket (storage request legacy-s3), and requiresFeatures names storage.object-storage.backup-exclusions; a host that does not fulfil the feature keeps listing this version and refuses it as an install or upgrade target. From 1.29.0 the upgrade moves Cloudly from 33.20.0 to 33.20.1. From 1.28.6 or earlier it also takes the changes of Cloudly 33.20.0, including the first start that creates the collection cloudly_runtime_log_inline_payloads and the log ledger rows Cloudly 33.19.2 and earlier refuse, so a return to 1.28.6 or an earlier template needs the backup taken before the upgrade; from 1.28.5 or 1.28.3, which run the Cloudly 33.19.1 image, it also takes the changes of Cloudly 33.19.2, and from 1.28.4 or earlier the template also gains the declaration. The rest of this changelog is that of 1.29.0, the Cloudly 33.20.0 template, and describes the changes of Cloudly 33.20.0 and the releases before it that an upgrade from 1.28.6 or earlier also takes: Move Cloudly to 33.20.0, which serves non-public hostnames to dcrouter alone, writes workload logs as segments and lets a service whose retained rollout plan cannot run be redeployed. A node's cluster ingress serves non-public hostnames, restricted to dcrouter: before, Cloudly compiled only PUBLIC domains into the cluster route table, so a STANDARD hostname got a hub route and a private DNS answer while nothing on the node served it. A domain whose sourceProfileRef is not PUBLIC now compiles, on a moved and on a Pallet-born cluster, into a route whose sourceAllowList is the new setting dcrouterForwardSourceAddresses, the addresses dcrouter's forwarded connections arrive from at a node (set in the dashboard's dcrouter panel or with updateSettings; @serve.zone/interfaces 32.55.1 refuses IPv4-mapped IPv6 prefixes there, write the IPv4 prefix). A change of the setting revokes and reissues the route and refreshes every cluster ingress at once, and the ingress-to-workload grant covers every domain's target port. A non-public domain is left out by name while public routes are still served: while dcrouterForwardSourceAddresses is unset, while the cluster's ingress has not registered route-source-allowlist, and while the live cluster relay speaks @serve.zone/interfaces below 32.55.0. To serve a non-public hostname, the cluster's Coreflow and CoreTraffic must run releases on @serve.zone/interfaces 32.55.0 or later, and CoreTraffic must register the capability. Public port mappings stay public-only. Workload log payloads are written as segments, one object per capture every few minutes instead of one per batch: one object per batch grew Cloudly's bucket by about 7,000 objects an hour. A committed batch keeps its payload inline in the database, in the new collection cloudly_runtime_log_inline_payloads, so it reads as soon as it is committed; the drain then writes a capture's inline payloads as one segment, runtime-logs/<serviceId>/<assignmentId>/<captureId>/<first>-<last>.jsonl, and only after the write moves the ledger rows onto it and removes the inline payloads. A segment is due 5 minutes after its capture's oldest inline payload was committed, at 1 MiB, when the capture's final batch is inline, and early, largest capture first, once all inline payloads pass 16 MiB; at 32 MiB the drain commits nothing more until segments are written, so the queue's bounds refuse new reports with the retryable log-intake-saturated. A stop writes no segment early, and the next start writes the inline payloads when due. Rows committed before this version name their own object as before and age out under the 30-day retention. All payload objects stay below runtime-logs/, the prefix this template declares non-backup. The ledger collection cloudly_runtime_log_batches gains no index, so a start builds none over the existing ledger. A spec-owned service whose retained rollout plan the current runtime cannot execute, such as a Swarm-era promotion written before @serve.zone/interfaces 23.0.0 made the sealed containerInvocation required, can be redeployed: its runtime producer refuses the plan image-plan-missing instead of failing its build step on every pass, and while no deployment operation is in flight the reserve preflight answers GO with the auditable RECOVERY_REDEPLOY_OVER_FAILED_RUNTIME warning, which names the plan and the rules it breaks, instead of NO-GO with CURRENT_RUNTIME_DIGEST_MISSING and RUNTIME_ATTESTATION_MISMATCH. An attested runtime, a mutable current reference, a valid plan over an unattested runtime and an operation in flight still block as before. Cloudly 33.20.0 adds no data step and keeps the data version 0.30.0, so an upgrade from 1.28.6 runs no data step; it changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.28.6 and requires Onebox 33.8.0. Its first start creates the collection cloudly_runtime_log_inline_payloads. On the platform Corestore 32.2.4 (NoSQLDB 10.6.1) a collection creation waits at most 5 s for the open transactions on the Cloudly database, and a transaction an earlier Cloudly left open by exiting before its stop completed (it logs that its shutdown deadline passed) holds that database until its session expires 30 minutes later, so that first start fails with EFENCE_BUSY until then: after an unclean stop of the previous Cloudly, start this version no earlier than 30 minutes after that exit. Every workload log batch this version commits names where its payload is, and Cloudly 33.19.2 and earlier refuse such a ledger row whenever they read it, so a return to 1.28.6 or an earlier template after this version committed a log batch requires the backup taken before the upgrade. Like 1.28.5 and 1.28.6, this template declares Cloudly's workload logs non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket, the object storage platformRequirements.s3 provisions (storage request legacy-s3), and requiresFeatures names storage.object-storage.backup-exclusions. A host that fulfils that feature leaves the objects under runtime-logs/ out of Cloudly's backups, and a restore brings back Cloudly's state, not its past workload logs: it neither restores nor removes objects under that prefix, and Cloudly reports a log batch whose payload it no longer has as a gap. A host that does not fulfil the feature keeps listing this version and refuses it as an install or upgrade target. From 1.28.6 the upgrade moves Cloudly from 33.19.2 to 33.20.0. From 1.28.5, which runs the Cloudly 33.19.1 image of 1.28.3, it also takes the changes of Cloudly 33.19.2; from 1.28.4 or earlier the template also gains the declaration. The rest of this changelog is that of 1.28.4, the Cloudly 33.19.2 template, and describes the changes of Cloudly 33.19.2 and the releases before it that an upgrade from 1.28.5 or earlier also takes: Move Cloudly to 33.19.2, a fixes-only patch over 33.19.1. A greenfield deployment, a legacy import's successor included, proves its namespace blank on the nodes of the Pallet clusters (phase switching or pallet) and nowhere else: a reservation whose service had no rollout plan yet asked every registered node, so a fleet that still keeps a Coreflow-owned Swarm cluster without a relay could never reserve one, and a legacy import's reserve preflight answered NO-GO with CORESTORE_INVENTORY_UNAVAILABLE and NAMESPACE_NOT_PROVEN_BLANK. The deployment preflight and reserveServiceDeployment read the same nodes; a Pallet node whose inventory cannot be read still blocks, a fleet with no Pallet cluster node refuses the reservation CORESTORE_INVENTORY_UNAVAILABLE, and an existing service's preflight still reads its plan's clusters. A greenfield rollout without a Pallet runtime spec is refused GREENFIELD_PALLET_RUNTIME_SPEC_REQUIRED, which is not retryable, instead of falling back to Coreflow placement onto Swarm nodes the namespace proof never covered: state the spec with setServiceRuntimeSpec, then promote again. A rollout with a Pallet spec plans onto the spec's nodes as before. A node's network compilation proves each workload lease once and each protected authority revision and allocation eligibility proof once per transaction (before, every lease was read up to three times per compilation and each read re-proved the same revision and proof, about 24 proof reads per endpoint), so a compilation with 100 endpoints reads 539 records instead of 2,839 (7.8 s instead of 19.8 s on Cloudly's test fixture), and every compilation signs and refuses exactly as before. Cloudly 33.19.2 adds no data step and no collection and keeps the data version 0.30.0, so an upgrade from 1.28.3 runs no data step; it changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.28.3 and requires Onebox 33.8.0. From 1.28.2 or earlier, the changes of Cloudly 33.18.3, 33.18.4, 33.18.5, 33.19.0 and 33.19.1 apply too: Runtime log intake no longer holds a node's request while its payload uploads: an admitted reportRuntimeAssignmentLogs costs one append transaction that queues the batch with its payload in the new collection cloudly_runtime_log_queue, and the node is answered once it commits. A background drain writes the payloads, up to 32 at once, and commits the ledger rows and removes the queue rows in coalesced transactions that fence no controller record; every other node request under a request budget counts as critical work, and the drain waits for it at most 1 s per step. A resend of a batch the ledger or the queue already holds is answered replay with nothing written, and while the queue is at its bounds (1024 batches, 64 MiB, 8 concurrent intakes) a report is refused at once with the retryable log-intake-saturated. Object writes list nothing (@lossless.org/client 1.23.3), workload logs are kept for at least 30 days whatever shorter logs.retentionMs a spec states, and one runtime log intake line, logged on its own timer at most once a minute, reports intake latency, queue depth, the drain and refusals. A node that applied its network head is no longer handed a successor that only drops the head's withdrawals, and a successor of the projection a node's receipt names lists only the tombstones its own step retires, so a node with more than 256 retired leases or handoffs stays within the contract's bound. Route verification reads the certificate a route presents on a connection of its own, without pooling or TLS session resumption, so it records the route's subject alternative names; a connection that exposes no certificate fails with TLS_PEER_CERTIFICATE_UNAVAILABLE and a certificate without names with TLS_SUBJECT_ALT_NAMES_MISSING. readServiceDeploymentStatus (@serve.zone/interfaces 32.54.0) reads a deployment operation's status without changing it, and a failed service deletion answers SERVICE_DELETION_FAILED and logs its cause. The first start after 1.28.2 or earlier creates the collection cloudly_runtime_log_queue. On the platform Corestore 32.2.4 (NoSQLDB 10.6.1) a collection creation waits at most 5 s for the open transactions on the Cloudly database, and a transaction an earlier Cloudly left open by exiting before its stop completed (it logs that its shutdown deadline passed) holds that database until its session expires 30 minutes later, so that first start fails with EFENCE_BUSY until then: after an unclean stop of the previous Cloudly, start this version no earlier than 30 minutes after that exit. Cloudly 33.19.1 adds no data step and keeps the data version 0.30.0, so an upgrade from 1.28.2, 1.28.1 or 1.28.0 runs no data step; it changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.28.2 and requires Onebox 33.8.0. From 1.28.1 or earlier, Cloudly 33.18.2 advances a node's network configuration only once the node's authenticated application receipt names its head. Upgrade every node to Pallet 36.3.0 or later, speaking @serve.zone/interfaces 32.50.0 or later, before this Cloudly update: a node that sends no application receipt, Pallet 36.2.1 or earlier, or whose session speaks @serve.zone/interfaces below 32.50.0, keeps the projection it holds and receives no successor. The receipt wire contract is unchanged. The changes of Cloudly 33.18.2 and the releases before it that an upgrade from 1.28.1 or earlier also takes are described in the changelog of cloudly@1.28.2, which repeats those of every earlier template; this changelog keeps their upgrade path only, because a catalog changelog is bounded at 65536 characters. From 1.2.1 or earlier, startup runs the forward-only data migration from 0.20.0 to 0.21.0 through the step retire-cluster-relay-acme and then the steps runtime-session-protocol-offer to 0.22.0, deployment-machine-user-grants to 0.23.0, corestore-control-token-state to 0.24.0, runtime-log-attempts to 0.25.0, cluster-node-hostname-intent to 0.26.0, deployment-operation-route-evidence to 0.27.0, service-secret-target-predecessor to 0.28.0, route-claim-absent-owners to 0.29.0 and bounded-runtime-history to 0.30.0; retire-cluster-relay-acme drops the relay certificate and DNS collections of Cloudly's retired Cloudflare and Let's Encrypt integration and logs every relay name whose A record Cloudly had written at Cloudflare: remove each such record at the provider. A cluster relay then requires a dcrouter that advertises dns.clientRecords and certificates.exactIssuance, SERVEZONE_SSLMODE=letsencrypt is refused (this template keeps external), and the typed API changes as the Breaking Changes of the Cloudly 33.0.0 changelog list; review them, stop Cloudly and take a verified backup before upgrading, because the 32.9 image refuses the migrated data and rollback requires a full restore. The upgrade is marked migration-required and backup-before-upgrade because hosts read these flags from the target version alone and every upgrade from 1.26.0 or earlier runs the 0.28.0 to 0.30.0 steps, one from 1.24.0 or earlier, and from 1.25.0 whose first start failed, the 0.25.0 to 0.28.0 steps before them, one from 1.19.0 or earlier the 0.24.0 to 0.25.0 step before that, one from 1.15.0 or earlier the 0.23.0 to 0.24.0 step before that, one from 1.8.0 or earlier the 0.22.0 to 0.23.0 step before that, one from 1.6.0 or earlier the 0.21.0 to 0.22.0 step before that and one from 1.2.1 or earlier the migration from 0.20.0 as well, and breaking and manual-review because every Cloudly before 33.17.3 refuses the 0.30.0 data, the 0.30.0 step deletes every archived deployment record, the 0.23.0 step removes stored grants forward-only, Cloudly 33.18.0 ignores APPSTORE_URL and loads the App Store catalog from its catalog registries and, from 1.7.0 or earlier, a username and password deployer, an administrator's deployment reservation and a machine identity or registry bearer issued earlier are refused. An installation on 1.1.10 or earlier also runs the 1.2.0 steps first, from 0.10.0, and requires completed Secrets v2 cutover retirement. Preserve existing user and storage identities, OIDC bindings, the encrypted keyring and WorkloadInit approval as dedicated root-readable files.",
|
|
9057
|
+
"breaking": true,
|
|
9058
|
+
"requiresManualReview": true,
|
|
9059
|
+
"migrationRequired": true,
|
|
9060
|
+
"backupBeforeUpgrade": true,
|
|
9061
|
+
"requiresFeatures": [
|
|
9062
|
+
"storage.database.backup-exclusions",
|
|
9063
|
+
"storage.object-storage.backup-exclusions"
|
|
9064
|
+
],
|
|
9065
|
+
"healthCheck": {
|
|
9066
|
+
"path": "/status",
|
|
9067
|
+
"port": 3000,
|
|
9068
|
+
"expectedStatus": 200
|
|
9069
|
+
}
|
|
9070
|
+
},
|
|
9071
|
+
"configDigest": "sha256:e97c235d8f27d9210d9828a83a61035a7b42b11cba667826f3c7e3dfb37b813c"
|
|
8868
9072
|
}
|
|
8869
9073
|
]
|
|
8870
9074
|
},
|
|
@@ -9638,11 +9842,11 @@
|
|
|
9638
9842
|
}
|
|
9639
9843
|
]
|
|
9640
9844
|
},
|
|
9641
|
-
"catalogDigest": "sha256:
|
|
9845
|
+
"catalogDigest": "sha256:85f53ee1fbb430c24ca61951fab760815ec6a52d633a9df0fc1ee790a07e017b",
|
|
9642
9846
|
"signatures": [
|
|
9643
9847
|
{
|
|
9644
9848
|
"keyId": "sha256:58956c3537cdf542430d6685261824776d6128ebb9a93b3d77b53f2d031ba854",
|
|
9645
|
-
"signature": "
|
|
9849
|
+
"signature": "b9d3b1b57abe7ff5597194994fbdac49e232344ff064fc7b2d3c5e4ad51da9c6a40a9abf2630608312f73577fc47189ac055d2e6c75861eecc7e11341c79de08"
|
|
9646
9850
|
}
|
|
9647
9851
|
]
|
|
9648
9852
|
}
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"major": 1,
|
|
5
5
|
"minor": 0
|
|
6
6
|
},
|
|
7
|
-
"revision":
|
|
8
|
-
"publishedAt": "2026-10-
|
|
9
|
-
"sourceRevision": "
|
|
7
|
+
"revision": 33000012000000,
|
|
8
|
+
"publishedAt": "2026-10-17T07:18:39.000Z",
|
|
9
|
+
"sourceRevision": "12931b30771f0258982cac18513be47a8793547f",
|
|
10
10
|
"apps": [
|
|
11
11
|
{
|
|
12
12
|
"info": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"Docs": "https://serve.zone"
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
|
-
"latestVersion": "1.
|
|
62
|
+
"latestVersion": "1.30.0",
|
|
63
63
|
"versions": [
|
|
64
64
|
{
|
|
65
65
|
"version": "1.0.0",
|
|
@@ -8864,6 +8864,210 @@
|
|
|
8864
8864
|
}
|
|
8865
8865
|
},
|
|
8866
8866
|
"configDigest": "sha256:5379de0dd94d6bddcb2541c3e95f70f6868619ab62bfd6e117b5bf7e11e58a2c"
|
|
8867
|
+
},
|
|
8868
|
+
{
|
|
8869
|
+
"version": "1.30.0",
|
|
8870
|
+
"config": {
|
|
8871
|
+
"image": "code.foss.global/serve.zone/cloudly@sha256:cd0bfc82575d9a4a9d0808e19ec61b0bf188d83729d66195afbf5f373164f16e",
|
|
8872
|
+
"targetPorts": [
|
|
8873
|
+
{
|
|
8874
|
+
"name": "web",
|
|
8875
|
+
"port": 3000,
|
|
8876
|
+
"protocol": "http",
|
|
8877
|
+
"default": true
|
|
8878
|
+
}
|
|
8879
|
+
],
|
|
8880
|
+
"envVars": [
|
|
8881
|
+
{
|
|
8882
|
+
"key": "SERVEZONE_ENVIRONMENT",
|
|
8883
|
+
"value": "production",
|
|
8884
|
+
"description": "Cloudly runtime environment.",
|
|
8885
|
+
"required": true
|
|
8886
|
+
},
|
|
8887
|
+
{
|
|
8888
|
+
"key": "SERVEZONE_PUBLIC_ORIGIN",
|
|
8889
|
+
"value": "https://${SERVICE_DOMAIN}",
|
|
8890
|
+
"description": "Canonical public Cloudly origin including protocol and optional public port.",
|
|
8891
|
+
"required": true
|
|
8892
|
+
},
|
|
8893
|
+
{
|
|
8894
|
+
"key": "SERVEZONE_LISTENPORT",
|
|
8895
|
+
"value": "3000",
|
|
8896
|
+
"description": "Internal Cloudly HTTP port inside the container.",
|
|
8897
|
+
"required": true
|
|
8898
|
+
},
|
|
8899
|
+
{
|
|
8900
|
+
"key": "SERVEZONE_SSLMODE",
|
|
8901
|
+
"value": "external",
|
|
8902
|
+
"description": "Use external TLS termination through Onebox or dcrouter.",
|
|
8903
|
+
"required": true
|
|
8904
|
+
},
|
|
8905
|
+
{
|
|
8906
|
+
"key": "SERVEZONE_SECRET_KEYRING_PATH",
|
|
8907
|
+
"value": "/run/secrets/cloudly-secrets-v2-keyring.json",
|
|
8908
|
+
"description": "Path to the Cloudly Secrets v2 keyring mounted by Onebox; the keyring value is never placed in the public environment.",
|
|
8909
|
+
"required": true
|
|
8910
|
+
},
|
|
8911
|
+
{
|
|
8912
|
+
"key": "SERVEZONE_SECRET_KEYRING_FILE",
|
|
8913
|
+
"description": "Existing Cloudly Secrets v2 keyring. Onebox stores it encrypted and mounts it only as a root-readable file; retain the existing keyring when upgrading.",
|
|
8914
|
+
"required": true,
|
|
8915
|
+
"secret": true,
|
|
8916
|
+
"delivery": {
|
|
8917
|
+
"type": "file",
|
|
8918
|
+
"targetPath": "/run/secrets/cloudly-secrets-v2-keyring.json",
|
|
8919
|
+
"uid": 0,
|
|
8920
|
+
"gid": 0,
|
|
8921
|
+
"mode": 256
|
|
8922
|
+
}
|
|
8923
|
+
},
|
|
8924
|
+
{
|
|
8925
|
+
"key": "SERVEZONE_WORKLOADINIT_APPROVAL_PATH",
|
|
8926
|
+
"value": "/run/secrets/cloudly-workloadinit-approval.json",
|
|
8927
|
+
"description": "Private WorkloadInit release-approval artifact mounted by Onebox for Coreflow secret-runtime registration.",
|
|
8928
|
+
"required": true
|
|
8929
|
+
},
|
|
8930
|
+
{
|
|
8931
|
+
"key": "SERVEZONE_WORKLOADINIT_APPROVAL_FILE",
|
|
8932
|
+
"description": "Operator-approved WorkloadInit release authority. Onebox stores it encrypted and mounts it only as a root-readable file.",
|
|
8933
|
+
"required": true,
|
|
8934
|
+
"secret": true,
|
|
8935
|
+
"delivery": {
|
|
8936
|
+
"type": "file",
|
|
8937
|
+
"targetPath": "/run/secrets/cloudly-workloadinit-approval.json",
|
|
8938
|
+
"uid": 0,
|
|
8939
|
+
"gid": 0,
|
|
8940
|
+
"mode": 256
|
|
8941
|
+
}
|
|
8942
|
+
},
|
|
8943
|
+
{
|
|
8944
|
+
"key": "MONGODB_URL",
|
|
8945
|
+
"value": "${MONGODB_URI}",
|
|
8946
|
+
"description": "Authenticated MongoDB wire-protocol URL provisioned by Onebox Corestore.",
|
|
8947
|
+
"required": true
|
|
8948
|
+
},
|
|
8949
|
+
{
|
|
8950
|
+
"key": "MONGODB_NAME",
|
|
8951
|
+
"value": "${MONGODB_DATABASE}",
|
|
8952
|
+
"description": "MongoDB wire-protocol database name provisioned by Onebox Corestore.",
|
|
8953
|
+
"required": true
|
|
8954
|
+
},
|
|
8955
|
+
{
|
|
8956
|
+
"key": "MONGODB_USER",
|
|
8957
|
+
"value": "${MONGODB_USERNAME}",
|
|
8958
|
+
"description": "MongoDB wire-protocol username provisioned by Onebox Corestore.",
|
|
8959
|
+
"required": true
|
|
8960
|
+
},
|
|
8961
|
+
{
|
|
8962
|
+
"key": "MONGODB_PASS",
|
|
8963
|
+
"value": "${MONGODB_PASSWORD}",
|
|
8964
|
+
"description": "MongoDB wire-protocol password provisioned by Onebox Corestore.",
|
|
8965
|
+
"required": true
|
|
8966
|
+
},
|
|
8967
|
+
{
|
|
8968
|
+
"key": "S3_ENDPOINT",
|
|
8969
|
+
"value": "onebox-corestore",
|
|
8970
|
+
"description": "S3 endpoint host provisioned by Onebox Corestore.",
|
|
8971
|
+
"required": true
|
|
8972
|
+
},
|
|
8973
|
+
{
|
|
8974
|
+
"key": "S3_PORT",
|
|
8975
|
+
"value": "9000",
|
|
8976
|
+
"description": "S3 endpoint port provisioned by Onebox Corestore.",
|
|
8977
|
+
"required": true
|
|
8978
|
+
},
|
|
8979
|
+
{
|
|
8980
|
+
"key": "S3_USESSL",
|
|
8981
|
+
"value": "false",
|
|
8982
|
+
"description": "Use plain HTTP for internal Corestore S3 traffic on the Onebox network.",
|
|
8983
|
+
"required": true
|
|
8984
|
+
},
|
|
8985
|
+
{
|
|
8986
|
+
"key": "S3_BUCKET",
|
|
8987
|
+
"value": "${S3_BUCKET}",
|
|
8988
|
+
"description": "Cloudly's own S3 bucket, provisioned by Onebox Corestore: registry storage and kept workload logs.",
|
|
8989
|
+
"required": true
|
|
8990
|
+
},
|
|
8991
|
+
{
|
|
8992
|
+
"key": "S3_ACCESSKEY",
|
|
8993
|
+
"value": "${S3_ACCESS_KEY}",
|
|
8994
|
+
"description": "S3 access key provisioned by Onebox Corestore.",
|
|
8995
|
+
"required": true
|
|
8996
|
+
},
|
|
8997
|
+
{
|
|
8998
|
+
"key": "S3_SECRETKEY",
|
|
8999
|
+
"value": "${S3_SECRET_KEY}",
|
|
9000
|
+
"description": "S3 secret key provisioned by Onebox Corestore.",
|
|
9001
|
+
"required": true
|
|
9002
|
+
}
|
|
9003
|
+
],
|
|
9004
|
+
"platformRequirements": {
|
|
9005
|
+
"mongodb": true,
|
|
9006
|
+
"s3": true
|
|
9007
|
+
},
|
|
9008
|
+
"backupExclusions": [
|
|
9009
|
+
{
|
|
9010
|
+
"storageRequest": "legacy-s3",
|
|
9011
|
+
"keyPrefixes": [
|
|
9012
|
+
"runtime-logs/"
|
|
9013
|
+
],
|
|
9014
|
+
"reason": "Workload logs; a restore brings back state, not past logs."
|
|
9015
|
+
}
|
|
9016
|
+
],
|
|
9017
|
+
"databaseBackupExclusions": [
|
|
9018
|
+
{
|
|
9019
|
+
"database": "mongodb",
|
|
9020
|
+
"collections": [
|
|
9021
|
+
"cloudly_runtime_log_attempts",
|
|
9022
|
+
"cloudly_runtime_log_batches",
|
|
9023
|
+
"cloudly_runtime_log_captures",
|
|
9024
|
+
"cloudly_runtime_log_inline_payloads",
|
|
9025
|
+
"cloudly_runtime_log_node_discards",
|
|
9026
|
+
"cloudly_runtime_log_queue"
|
|
9027
|
+
],
|
|
9028
|
+
"reason": "Workload log ledger; a restore brings back state, not past logs."
|
|
9029
|
+
}
|
|
9030
|
+
],
|
|
9031
|
+
"platformOidc": {
|
|
9032
|
+
"redirectPath": "/auth/oidc/callback",
|
|
9033
|
+
"roles": [
|
|
9034
|
+
{
|
|
9035
|
+
"id": "admin",
|
|
9036
|
+
"label": "Administrator",
|
|
9037
|
+
"description": "Full Cloudly administration for this hosted instance."
|
|
9038
|
+
},
|
|
9039
|
+
{
|
|
9040
|
+
"id": "user",
|
|
9041
|
+
"label": "User",
|
|
9042
|
+
"description": "Standard authenticated Cloudly access for this hosted instance."
|
|
9043
|
+
}
|
|
9044
|
+
],
|
|
9045
|
+
"environmentVariables": {
|
|
9046
|
+
"issuerUrl": "SERVEZONE_PLATFORM_OIDC_ISSUER",
|
|
9047
|
+
"clientId": "SERVEZONE_PLATFORM_OIDC_CLIENT_ID",
|
|
9048
|
+
"clientSecret": "SERVEZONE_PLATFORM_OIDC_CLIENT_SECRET",
|
|
9049
|
+
"redirectUri": "SERVEZONE_PLATFORM_OIDC_REDIRECT_URI",
|
|
9050
|
+
"audience": "SERVEZONE_PLATFORM_OIDC_AUDIENCE"
|
|
9051
|
+
},
|
|
9052
|
+
"clientAuthenticationMethod": "client_secret_basic"
|
|
9053
|
+
},
|
|
9054
|
+
"minOneboxVersion": "33.8.0",
|
|
9055
|
+
"changelog": "Move Cloudly to 33.21.0, a feature release over 33.20.4: Cloudly's runtime log ledger is no longer part of its backups, and a restore keeps the logs of every running workload. This template declares the six collections of the ledger, cloudly_runtime_log_attempts, cloudly_runtime_log_batches, cloudly_runtime_log_captures, cloudly_runtime_log_inline_payloads, cloudly_runtime_log_node_discards and cloudly_runtime_log_queue, as databaseBackupExclusions of its MongoDB database: a backup exports them with their indexes and without their documents, so a restore brings them back empty while the slots and the workloads they run stay. The ledger holds most of a long-running Cloudly's database, so its backups shrink to the state they protect. Every Cloudly 33.21.0 start writes the log record of each running attempt that has none, before it serves node requests, so the nodes' next batches are kept again instead of being refused as attempts whose logs Cloudly does not keep; the first batch of each capture after a restore states the numbers the restore lost as a gap. A start whose attempts all have their records writes nothing, and an attempt whose record cannot be written is named in a warning and tried again at the next start. requiresFeatures names storage.database.backup-exclusions beside storage.object-storage.backup-exclusions: a host that does not fulfil the database feature keeps listing this version and refuses it as an install or upgrade target, and its newest Cloudly target stays 1.29.4; a host without the object-storage feature keeps 1.28.4. Like 1.29.4, this template declares Cloudly's workload log objects non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket (storage request legacy-s3). Cloudly 33.21.0 adds no data step and no index and keeps the data version 0.31.0, so an upgrade from 1.29.4, 1.29.3 or 1.29.2 runs no data step; it adds no collection, and the template requires Onebox 33.8.0. From 1.29.4 the upgrade moves Cloudly from 33.20.4 to 33.21.0. The rest of this changelog is that of 1.29.4, the Cloudly 33.20.4 template, and describes the changes of Cloudly 33.20.4 and the releases before it that an upgrade from 1.29.3 or earlier also takes: Move Cloudly to 33.20.4, a fixes-only patch over 33.20.3: the runtime log sweep completes again over a large ledger and no longer keeps other controller transactions retrying. Over a ledger of about 140,000 rows every sweep pass failed after about 126 s with the transaction lifetime exceeded, because two of its reads cost the whole ledger rather than their own work: the walk over dropped rows was sorted by id, so it walked the id index over every row to find the few dropped ones, and the orphan listing's held check asked for either state at once, so it read every row of a capture for each of up to 1,000 listed keys in one transaction. Such a step outlived the database's 60 s transaction lifetime and was retried for 120 s, and since every step fences the controller record, other controller transactions write-conflicted for about two of every three minutes and managed VPN credentials missed their 6 s budget at slot starts. Now the sweep's ledger walks are unsorted, the held check reads one state at a time and stops at the first row, and whether a dropped attempt still has rows is probed instead of counted. Every sweep step is one controller transaction of at most 16 rows or keys (was 64; the held check took up to 1,000 keys in one), bounded by a 15 s deadline that includes its retries and commit, so a step that cannot finish ends the pass at once and the next pass continues after the last committed step; before each step the sweep waits for the critical node requests in flight, as the log drain does. No index is added, so the first start builds none. Cloudly 33.20.4 adds no data step and keeps the data version 0.31.0, so an upgrade from 1.29.3 or 1.29.2 runs no data step; it adds no collection, changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.29.3 and requires Onebox 33.8.0. Like 1.29.3, this template declares Cloudly's workload logs non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket (storage request legacy-s3), and requiresFeatures names storage.object-storage.backup-exclusions; a host that does not fulfil the feature keeps listing this version and refuses it as an install or upgrade target. From 1.29.3 the upgrade moves Cloudly from 33.20.3 to 33.20.4. From 1.29.2 it also takes the changes of Cloudly 33.20.3. From 1.29.1 or earlier it also takes the changes of Cloudly 33.20.2, including the forward-only data step service-reconciliation-observations from 0.30.0 to 0.31.0, which every Cloudly before 33.20.2 refuses, so a return to 1.29.1 or an earlier template means restoring the database backup taken before the upgrade; from 1.29.0 also those of Cloudly 33.20.1; from 1.28.6 or earlier also those of Cloudly 33.20.0, including the first start that creates the collection cloudly_runtime_log_inline_payloads and the log ledger rows Cloudly 33.19.2 and earlier refuse; from 1.28.5 or 1.28.3, which run the Cloudly 33.19.1 image, also those of Cloudly 33.19.2, and from 1.28.4 or earlier the template also gains the declaration. The rest of this changelog is that of 1.29.3, the Cloudly 33.20.3 template, and describes the changes of Cloudly 33.20.3 and the releases before it that an upgrade from 1.29.2 or earlier also takes: Move Cloudly to 33.20.3, a fixes-only patch over 33.20.2: a service that already holds a Corestore binding of a capability can adopt its own legacy resource of that capability into that binding. Services deployed before Corestore 32 hold a canonical Corestore database and bucket binding created by the generic path, and Corestore (since 34.1.4) refuses any binding beside an unauthorized legacy resource of the capability (legacy-adoption-pending). adoptCorestoreLegacyResources refused such a service binding-exists, a tombstone keeps the service's binding slot and a generic upsert cannot write the adoption, so no path could adopt the resource and the service stayed down. Now, when the legacy owner adopts its own resource (no successorServiceId) and holds a binding of the capability that is enabled, not tombstoned, on the canonical Corestore provider and adopts nothing yet, and the binding with the adoption is valid (a bucket binding names the legacy bucket and its retention intent exactly, a database binding carries neither), the adoption is written onto that binding in the adoption's transaction, a compare-and-set on the binding as it was read; its status returns to requested with its reason cleared, the resource's claim is written, and the plan names the binding's id. Every other binding the adopting service holds still refuses binding-exists: another bucket or retention intent, a database binding with a bucket, another provider, a tombstoned or disabled binding, one adopting another resource, and any binding of a named successor. Adopting again answers the marked binding unchanged. Corestore needs no change: its offline migration reads the plan as for a created binding. Cloudly 33.20.3 adds no data step and keeps the data version 0.31.0, so an upgrade from 1.29.2 runs no data step; it adds no collection, changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.29.2 and requires Onebox 33.8.0. Like 1.29.2, this template declares Cloudly's workload logs non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket (storage request legacy-s3), and requiresFeatures names storage.object-storage.backup-exclusions; a host that does not fulfil the feature keeps listing this version and refuses it as an install or upgrade target. From 1.29.2 the upgrade moves Cloudly from 33.20.2 to 33.20.3. From 1.29.1 or earlier it also takes the changes of Cloudly 33.20.2, including the forward-only data step service-reconciliation-observations from 0.30.0 to 0.31.0, which every Cloudly before 33.20.2 refuses, so a return to 1.29.1 or an earlier template means restoring the database backup taken before the upgrade; from 1.29.0 also those of Cloudly 33.20.1; from 1.28.6 or earlier also those of Cloudly 33.20.0, including the first start that creates the collection cloudly_runtime_log_inline_payloads and the log ledger rows Cloudly 33.19.2 and earlier refuse; from 1.28.5 or 1.28.3, which run the Cloudly 33.19.1 image, also those of Cloudly 33.19.2, and from 1.28.4 or earlier the template also gains the declaration. The rest of this changelog is that of 1.29.2, the Cloudly 33.20.2 template, and describes the changes of Cloudly 33.20.2 and the releases before it that an upgrade from 1.29.1 or earlier also takes: Move Cloudly to 33.20.2, which keeps its registry storage bounded, keeps a running replica when a rollout asks for what it already runs, and no longer loses gateway, mail and CoreMail status while a service lifecycle lease is held; its startup runs the forward-only data step to 0.31.0. A storage retention of the registry runs 3 minutes after start, then daily, or hourly while work remains: before, every tag push recorded an image release that protected its manifests and blobs forever, a greenfield cleanup left its repository behind, blobs no manifest referenced were never collected, and the release rows of deleted services kept protecting their images. A release stays while any service's plan names it or its repository root, any service that is not immutable runs a tag naming its root, a deployment operation still needs it (not terminal, or failed retryably within 7 days) or an unfinished rollout or adoption names it; besides, its service keeps its 3 most recently promoted accepted releases (its rollback targets) and every release observed or promoted within 7 days. Every other release is pruned with the tags naming its root and its image versions, at most 200 a pass. Manifests and blobs nothing reaches any more are deleted once they are older than 24 hours and unused for 24 hours, blobs only when every repository could be judged and Cloudly has served for an hour, at most 20,000 objects a pass; a repository that cannot be read is named in the pass's summary line and skipped, and repositories no service maps to are reported and kept. Every deletion runs in holds of the registry mutation lock that take no new item once 2 seconds passed, so the pushes queued meanwhile go between holds. The per-push prune is gone: after every tagged push it read every repository while holding that lock, and it could delete the child manifests of a multi-platform push in flight. A service deletion deletes only what its own repository owns, its manifests and then its tags, without reading any other repository and without deleting a blob, so a broken repository elsewhere no longer stops it; a greenfield cleanup now retires the provisional service's registry state too, while the operation's reservation holds the repository. The registry (@push.rocks/smartregistry 2.13.0) reads its storage strictly, refuses a manifest PUT that names a blob or child manifest it does not hold with 400 MANIFEST_BLOB_UNKNOWN (a client pushes blobs and children first), collects abandoned upload chunks and serves blob byte ranges, so a layer pull can resume. A rollout, or a retry of one, whose image and configuration a replica already runs keeps that replica running instead of replacing it: whether an incumbent runs a desire is judged by its run's identity or by its content (registry host, repository and image digest, platform, config reference, network attachment, secret authority and storage claims). Deliberately, a spec change that moves only placement or network policy (publicEgress, platformEndpointIds, published ports) no longer restarts a replica, because Pallet applies a new network projection to the running workload in place; upgrading Cloudly restarts no replica. Gateway, mail and CoreMail status reported while a service lifecycle lease is held is no longer refused and dropped after 12 retries: reconciliation status is observation and lives beside the service document, one record per observer in the new collection cloudly_service_reconciliation_stages, the newest report winning, and every API answer that carries a service joins it back as data.reconciliationStatus, so the dashboard reads it as before. Cloudly 33.20.2 adds the forward-only data step service-reconciliation-observations from 0.30.0 to 0.31.0, which moves each readable stage of every service's status into that collection and unsets the status in the service document; it changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.29.1 and requires Onebox 33.8.0. The step has no way back: every Cloudly before 33.20.2, 33.20.1 included, refuses to start on data version 0.31.0, so a return to 1.29.1 or an earlier template means restoring the database backup taken before the upgrade, and the registry manifests and blobs the retention deleted by then do not come back with a restored database. Like 1.29.1, this template declares Cloudly's workload logs non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket (storage request legacy-s3), and requiresFeatures names storage.object-storage.backup-exclusions; a host that does not fulfil the feature keeps listing this version and refuses it as an install or upgrade target. From 1.29.1 the upgrade moves Cloudly from 33.20.1 to 33.20.2 and runs the step to 0.31.0. From 1.29.0 it also takes the changes of Cloudly 33.20.1; from 1.28.6 or earlier also those of Cloudly 33.20.0, including the first start that creates the collection cloudly_runtime_log_inline_payloads and the log ledger rows Cloudly 33.19.2 and earlier refuse; from 1.28.5 or 1.28.3, which run the Cloudly 33.19.1 image, also those of Cloudly 33.19.2, and from 1.28.4 or earlier the template also gains the declaration. The rest of this changelog is that of 1.29.1, the Cloudly 33.20.1 template, and describes the changes of Cloudly 33.20.1 and the releases before it that an upgrade from 1.29.0 or earlier also takes: Move Cloudly to 33.20.1, a fixes-only patch over 33.20.0: a Cloudly restart no longer blanks a cluster's ingress routes, a revocation withdraws exactly the routes it revokes, and a Pallet registration Cloudly cannot serve for a reason of its own is refused as momentary. After each cold start of Cloudly 33.20.0 the cluster relay applied two route tables of 0 routes before the full one, and every site of the cluster answered 502 for about 2.5 minutes. Two causes, both fixed. First, Cloudly published an empty revocation as its first table of a cluster, because it held no table of it yet; now the first pull or refresh compiles the plan and issues the full table, and a relay's read that comes before it waits up to 6 s for it and is otherwise answered { table: null }, so the relay keeps the table it holds and the full one follows as a push. Second, every node registers again after a restart, which opens a new session, and a route endpoint required an observation reported under the current session, so every route waited until each node reported anew. A reporter is now one node instance under one credential generation (nodeInstanceNonce, Pallet's per-process nonce, stored on every session record): a node instance that only registered again keeps its observations, its cutover uplink endpoint and its cluster ingress registration authority until it reports anew, while a new Pallet process, a new credential generation or a deleted node supersedes them at once. A route's identity names the node instance and credential generation instead of the session id and epoch, so such a registration changes no route. Route sources that cannot be read right after a start keep the relay's table for at most 180 s, logged once per cluster; then one line names the clusters still waiting and their routes are withdrawn as before. A revocation is now the held table less the revoked routes and port routes, issued and pushed at once: before, any revoked route identity made Cloudly publish an empty table for the whole cluster before it fetched certificates for the next one, so one node registering again or one changed route blanked every site of the cluster. A revoked ingress designation still withdraws everything, and route sources that cannot be read still withdraw every route. A Pallet registration that fails for a reason of Cloudly's own (a database connection lost, a write conflict, a race with a credential being applied, a controller not ready yet or any unnamed failure) is answered controller-unavailable and a spent budget controller-deadline, the @serve.zone/interfaces 32.57.0 runtime session refusal contract, so the node waits and registers again instead of ending its session; before, only a spent budget or a stopping controller carried the refusal payload, and the node read every other failure as a verdict. Only the named verdicts carry no payload: an inactive credential, a deleted, retiring (node-retiring) or foreign node, an unverifiable carrier, a replay that states another registration (registration-replay-mismatch), a moved controller identity, an invalid body. A credential check whose store cannot be read now fails instead of reading as a credential that does not verify, so a registration whose carrier Cloudly could not check is momentary too. A request is answered at its budget even when its work ignores the abort signal, and each registration refusal is logged once per node and reason, value-free, until the node's next accepted registration. Cloudly 33.20.1 adds no data step and keeps the data version 0.30.0, so an upgrade from 1.29.0 runs no data step; it adds no collection, changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.29.0 and requires Onebox 33.8.0. Like 1.29.0, this template declares Cloudly's workload logs non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket (storage request legacy-s3), and requiresFeatures names storage.object-storage.backup-exclusions; a host that does not fulfil the feature keeps listing this version and refuses it as an install or upgrade target. From 1.29.0 the upgrade moves Cloudly from 33.20.0 to 33.20.1. From 1.28.6 or earlier it also takes the changes of Cloudly 33.20.0, including the first start that creates the collection cloudly_runtime_log_inline_payloads and the log ledger rows Cloudly 33.19.2 and earlier refuse, so a return to 1.28.6 or an earlier template needs the backup taken before the upgrade; from 1.28.5 or 1.28.3, which run the Cloudly 33.19.1 image, it also takes the changes of Cloudly 33.19.2, and from 1.28.4 or earlier the template also gains the declaration. The rest of this changelog is that of 1.29.0, the Cloudly 33.20.0 template, and describes the changes of Cloudly 33.20.0 and the releases before it that an upgrade from 1.28.6 or earlier also takes: Move Cloudly to 33.20.0, which serves non-public hostnames to dcrouter alone, writes workload logs as segments and lets a service whose retained rollout plan cannot run be redeployed. A node's cluster ingress serves non-public hostnames, restricted to dcrouter: before, Cloudly compiled only PUBLIC domains into the cluster route table, so a STANDARD hostname got a hub route and a private DNS answer while nothing on the node served it. A domain whose sourceProfileRef is not PUBLIC now compiles, on a moved and on a Pallet-born cluster, into a route whose sourceAllowList is the new setting dcrouterForwardSourceAddresses, the addresses dcrouter's forwarded connections arrive from at a node (set in the dashboard's dcrouter panel or with updateSettings; @serve.zone/interfaces 32.55.1 refuses IPv4-mapped IPv6 prefixes there, write the IPv4 prefix). A change of the setting revokes and reissues the route and refreshes every cluster ingress at once, and the ingress-to-workload grant covers every domain's target port. A non-public domain is left out by name while public routes are still served: while dcrouterForwardSourceAddresses is unset, while the cluster's ingress has not registered route-source-allowlist, and while the live cluster relay speaks @serve.zone/interfaces below 32.55.0. To serve a non-public hostname, the cluster's Coreflow and CoreTraffic must run releases on @serve.zone/interfaces 32.55.0 or later, and CoreTraffic must register the capability. Public port mappings stay public-only. Workload log payloads are written as segments, one object per capture every few minutes instead of one per batch: one object per batch grew Cloudly's bucket by about 7,000 objects an hour. A committed batch keeps its payload inline in the database, in the new collection cloudly_runtime_log_inline_payloads, so it reads as soon as it is committed; the drain then writes a capture's inline payloads as one segment, runtime-logs/<serviceId>/<assignmentId>/<captureId>/<first>-<last>.jsonl, and only after the write moves the ledger rows onto it and removes the inline payloads. A segment is due 5 minutes after its capture's oldest inline payload was committed, at 1 MiB, when the capture's final batch is inline, and early, largest capture first, once all inline payloads pass 16 MiB; at 32 MiB the drain commits nothing more until segments are written, so the queue's bounds refuse new reports with the retryable log-intake-saturated. A stop writes no segment early, and the next start writes the inline payloads when due. Rows committed before this version name their own object as before and age out under the 30-day retention. All payload objects stay below runtime-logs/, the prefix this template declares non-backup. The ledger collection cloudly_runtime_log_batches gains no index, so a start builds none over the existing ledger. A spec-owned service whose retained rollout plan the current runtime cannot execute, such as a Swarm-era promotion written before @serve.zone/interfaces 23.0.0 made the sealed containerInvocation required, can be redeployed: its runtime producer refuses the plan image-plan-missing instead of failing its build step on every pass, and while no deployment operation is in flight the reserve preflight answers GO with the auditable RECOVERY_REDEPLOY_OVER_FAILED_RUNTIME warning, which names the plan and the rules it breaks, instead of NO-GO with CURRENT_RUNTIME_DIGEST_MISSING and RUNTIME_ATTESTATION_MISMATCH. An attested runtime, a mutable current reference, a valid plan over an unattested runtime and an operation in flight still block as before. Cloudly 33.20.0 adds no data step and keeps the data version 0.30.0, so an upgrade from 1.28.6 runs no data step; it changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.28.6 and requires Onebox 33.8.0. Its first start creates the collection cloudly_runtime_log_inline_payloads. On the platform Corestore 32.2.4 (NoSQLDB 10.6.1) a collection creation waits at most 5 s for the open transactions on the Cloudly database, and a transaction an earlier Cloudly left open by exiting before its stop completed (it logs that its shutdown deadline passed) holds that database until its session expires 30 minutes later, so that first start fails with EFENCE_BUSY until then: after an unclean stop of the previous Cloudly, start this version no earlier than 30 minutes after that exit. Every workload log batch this version commits names where its payload is, and Cloudly 33.19.2 and earlier refuse such a ledger row whenever they read it, so a return to 1.28.6 or an earlier template after this version committed a log batch requires the backup taken before the upgrade. Like 1.28.5 and 1.28.6, this template declares Cloudly's workload logs non-backup data: backupExclusions names the key prefix runtime-logs/ of Cloudly's own bucket, the object storage platformRequirements.s3 provisions (storage request legacy-s3), and requiresFeatures names storage.object-storage.backup-exclusions. A host that fulfils that feature leaves the objects under runtime-logs/ out of Cloudly's backups, and a restore brings back Cloudly's state, not its past workload logs: it neither restores nor removes objects under that prefix, and Cloudly reports a log batch whose payload it no longer has as a gap. A host that does not fulfil the feature keeps listing this version and refuses it as an install or upgrade target. From 1.28.6 the upgrade moves Cloudly from 33.19.2 to 33.20.0. From 1.28.5, which runs the Cloudly 33.19.1 image of 1.28.3, it also takes the changes of Cloudly 33.19.2; from 1.28.4 or earlier the template also gains the declaration. The rest of this changelog is that of 1.28.4, the Cloudly 33.19.2 template, and describes the changes of Cloudly 33.19.2 and the releases before it that an upgrade from 1.28.5 or earlier also takes: Move Cloudly to 33.19.2, a fixes-only patch over 33.19.1. A greenfield deployment, a legacy import's successor included, proves its namespace blank on the nodes of the Pallet clusters (phase switching or pallet) and nowhere else: a reservation whose service had no rollout plan yet asked every registered node, so a fleet that still keeps a Coreflow-owned Swarm cluster without a relay could never reserve one, and a legacy import's reserve preflight answered NO-GO with CORESTORE_INVENTORY_UNAVAILABLE and NAMESPACE_NOT_PROVEN_BLANK. The deployment preflight and reserveServiceDeployment read the same nodes; a Pallet node whose inventory cannot be read still blocks, a fleet with no Pallet cluster node refuses the reservation CORESTORE_INVENTORY_UNAVAILABLE, and an existing service's preflight still reads its plan's clusters. A greenfield rollout without a Pallet runtime spec is refused GREENFIELD_PALLET_RUNTIME_SPEC_REQUIRED, which is not retryable, instead of falling back to Coreflow placement onto Swarm nodes the namespace proof never covered: state the spec with setServiceRuntimeSpec, then promote again. A rollout with a Pallet spec plans onto the spec's nodes as before. A node's network compilation proves each workload lease once and each protected authority revision and allocation eligibility proof once per transaction (before, every lease was read up to three times per compilation and each read re-proved the same revision and proof, about 24 proof reads per endpoint), so a compilation with 100 endpoints reads 539 records instead of 2,839 (7.8 s instead of 19.8 s on Cloudly's test fixture), and every compilation signs and refuses exactly as before. Cloudly 33.19.2 adds no data step and no collection and keeps the data version 0.30.0, so an upgrade from 1.28.3 runs no data step; it changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.28.3 and requires Onebox 33.8.0. From 1.28.2 or earlier, the changes of Cloudly 33.18.3, 33.18.4, 33.18.5, 33.19.0 and 33.19.1 apply too: Runtime log intake no longer holds a node's request while its payload uploads: an admitted reportRuntimeAssignmentLogs costs one append transaction that queues the batch with its payload in the new collection cloudly_runtime_log_queue, and the node is answered once it commits. A background drain writes the payloads, up to 32 at once, and commits the ledger rows and removes the queue rows in coalesced transactions that fence no controller record; every other node request under a request budget counts as critical work, and the drain waits for it at most 1 s per step. A resend of a batch the ledger or the queue already holds is answered replay with nothing written, and while the queue is at its bounds (1024 batches, 64 MiB, 8 concurrent intakes) a report is refused at once with the retryable log-intake-saturated. Object writes list nothing (@lossless.org/client 1.23.3), workload logs are kept for at least 30 days whatever shorter logs.retentionMs a spec states, and one runtime log intake line, logged on its own timer at most once a minute, reports intake latency, queue depth, the drain and refusals. A node that applied its network head is no longer handed a successor that only drops the head's withdrawals, and a successor of the projection a node's receipt names lists only the tombstones its own step retires, so a node with more than 256 retired leases or handoffs stays within the contract's bound. Route verification reads the certificate a route presents on a connection of its own, without pooling or TLS session resumption, so it records the route's subject alternative names; a connection that exposes no certificate fails with TLS_PEER_CERTIFICATE_UNAVAILABLE and a certificate without names with TLS_SUBJECT_ALT_NAMES_MISSING. readServiceDeploymentStatus (@serve.zone/interfaces 32.54.0) reads a deployment operation's status without changing it, and a failed service deletion answers SERVICE_DELETION_FAILED and logs its cause. The first start after 1.28.2 or earlier creates the collection cloudly_runtime_log_queue. On the platform Corestore 32.2.4 (NoSQLDB 10.6.1) a collection creation waits at most 5 s for the open transactions on the Cloudly database, and a transaction an earlier Cloudly left open by exiting before its stop completed (it logs that its shutdown deadline passed) holds that database until its session expires 30 minutes later, so that first start fails with EFENCE_BUSY until then: after an unclean stop of the previous Cloudly, start this version no earlier than 30 minutes after that exit. Cloudly 33.19.1 adds no data step and keeps the data version 0.30.0, so an upgrade from 1.28.2, 1.28.1 or 1.28.0 runs no data step; it changes neither the Dockerfile nor the qenv.yml requirements, and the runtime contract is unchanged from 1.28.2 and requires Onebox 33.8.0. From 1.28.1 or earlier, Cloudly 33.18.2 advances a node's network configuration only once the node's authenticated application receipt names its head. Upgrade every node to Pallet 36.3.0 or later, speaking @serve.zone/interfaces 32.50.0 or later, before this Cloudly update: a node that sends no application receipt, Pallet 36.2.1 or earlier, or whose session speaks @serve.zone/interfaces below 32.50.0, keeps the projection it holds and receives no successor. The receipt wire contract is unchanged. The changes of Cloudly 33.18.2 and the releases before it that an upgrade from 1.28.1 or earlier also takes are described in the changelog of cloudly@1.28.2, which repeats those of every earlier template; this changelog keeps their upgrade path only, because a catalog changelog is bounded at 65536 characters. From 1.2.1 or earlier, startup runs the forward-only data migration from 0.20.0 to 0.21.0 through the step retire-cluster-relay-acme and then the steps runtime-session-protocol-offer to 0.22.0, deployment-machine-user-grants to 0.23.0, corestore-control-token-state to 0.24.0, runtime-log-attempts to 0.25.0, cluster-node-hostname-intent to 0.26.0, deployment-operation-route-evidence to 0.27.0, service-secret-target-predecessor to 0.28.0, route-claim-absent-owners to 0.29.0 and bounded-runtime-history to 0.30.0; retire-cluster-relay-acme drops the relay certificate and DNS collections of Cloudly's retired Cloudflare and Let's Encrypt integration and logs every relay name whose A record Cloudly had written at Cloudflare: remove each such record at the provider. A cluster relay then requires a dcrouter that advertises dns.clientRecords and certificates.exactIssuance, SERVEZONE_SSLMODE=letsencrypt is refused (this template keeps external), and the typed API changes as the Breaking Changes of the Cloudly 33.0.0 changelog list; review them, stop Cloudly and take a verified backup before upgrading, because the 32.9 image refuses the migrated data and rollback requires a full restore. The upgrade is marked migration-required and backup-before-upgrade because hosts read these flags from the target version alone and every upgrade from 1.26.0 or earlier runs the 0.28.0 to 0.30.0 steps, one from 1.24.0 or earlier, and from 1.25.0 whose first start failed, the 0.25.0 to 0.28.0 steps before them, one from 1.19.0 or earlier the 0.24.0 to 0.25.0 step before that, one from 1.15.0 or earlier the 0.23.0 to 0.24.0 step before that, one from 1.8.0 or earlier the 0.22.0 to 0.23.0 step before that, one from 1.6.0 or earlier the 0.21.0 to 0.22.0 step before that and one from 1.2.1 or earlier the migration from 0.20.0 as well, and breaking and manual-review because every Cloudly before 33.17.3 refuses the 0.30.0 data, the 0.30.0 step deletes every archived deployment record, the 0.23.0 step removes stored grants forward-only, Cloudly 33.18.0 ignores APPSTORE_URL and loads the App Store catalog from its catalog registries and, from 1.7.0 or earlier, a username and password deployer, an administrator's deployment reservation and a machine identity or registry bearer issued earlier are refused. An installation on 1.1.10 or earlier also runs the 1.2.0 steps first, from 0.10.0, and requires completed Secrets v2 cutover retirement. Preserve existing user and storage identities, OIDC bindings, the encrypted keyring and WorkloadInit approval as dedicated root-readable files.",
|
|
9056
|
+
"breaking": true,
|
|
9057
|
+
"requiresManualReview": true,
|
|
9058
|
+
"migrationRequired": true,
|
|
9059
|
+
"backupBeforeUpgrade": true,
|
|
9060
|
+
"requiresFeatures": [
|
|
9061
|
+
"storage.database.backup-exclusions",
|
|
9062
|
+
"storage.object-storage.backup-exclusions"
|
|
9063
|
+
],
|
|
9064
|
+
"healthCheck": {
|
|
9065
|
+
"path": "/status",
|
|
9066
|
+
"port": 3000,
|
|
9067
|
+
"expectedStatus": 200
|
|
9068
|
+
}
|
|
9069
|
+
},
|
|
9070
|
+
"configDigest": "sha256:e97c235d8f27d9210d9828a83a61035a7b42b11cba667826f3c7e3dfb37b813c"
|
|
8867
9071
|
}
|
|
8868
9072
|
]
|
|
8869
9073
|
},
|
|
@@ -13,7 +13,8 @@ export declare function normalizeAppStoreVolumes(configArg: TCatalogVersionConfi
|
|
|
13
13
|
* The config's storage is judged by the interfaces contract first. Legacy `volumes` become
|
|
14
14
|
* filesystem requests of the reserved `legacyFilesystem` class, `platformRequirements.s3` becomes
|
|
15
15
|
* the `legacy-s3` object-storage request, `backupExclusions` become the declared non-backup key
|
|
16
|
-
* prefixes by request id,
|
|
16
|
+
* prefixes by request id, `databaseBackupExclusions` the collections a backup exports without
|
|
17
|
+
* documents by database, and explicit requests and backup exclusions fail closed unless every
|
|
17
18
|
* storage feature they need is advertised by the target runtime.
|
|
18
19
|
*/
|
|
19
20
|
export declare function normalizeAppStoreStorageConfig(configArg: TCatalogVersionConfig, optionsArg: IAppStoreStorageNormalizationOptions, labelArg?: string): INormalizedAppStoreStorageConfig;
|
|
@@ -39,10 +39,12 @@ const createLegacyVolumeRequestId = (volumeArg, indexArg, labelArg) => {
|
|
|
39
39
|
return requestId;
|
|
40
40
|
};
|
|
41
41
|
/** The storage feature ids a config needs, in the strictly increasing order `requiresFeatures` lists them. */
|
|
42
|
-
const deriveRequiredFeatureIds = (explicitRequestsArg, backupExclusionsArg) => {
|
|
42
|
+
const deriveRequiredFeatureIds = (explicitRequestsArg, backupExclusionsArg, databaseBackupExclusionsArg) => {
|
|
43
43
|
const required = [];
|
|
44
44
|
if (explicitRequestsArg.length > 0)
|
|
45
45
|
required.push(storageFeatureIds.bindings);
|
|
46
|
+
if (databaseBackupExclusionsArg.length > 0)
|
|
47
|
+
required.push(storageFeatureIds.databaseBackupExclusions);
|
|
46
48
|
if (explicitRequestsArg.some((requestArg) => requestArg.kind === 'filesystem')) {
|
|
47
49
|
required.push(storageFeatureIds.filesystem);
|
|
48
50
|
}
|
|
@@ -70,7 +72,8 @@ export function normalizeAppStoreVolumes(configArg, labelArg = defaultLabel) {
|
|
|
70
72
|
* The config's storage is judged by the interfaces contract first. Legacy `volumes` become
|
|
71
73
|
* filesystem requests of the reserved `legacyFilesystem` class, `platformRequirements.s3` becomes
|
|
72
74
|
* the `legacy-s3` object-storage request, `backupExclusions` become the declared non-backup key
|
|
73
|
-
* prefixes by request id,
|
|
75
|
+
* prefixes by request id, `databaseBackupExclusions` the collections a backup exports without
|
|
76
|
+
* documents by database, and explicit requests and backup exclusions fail closed unless every
|
|
74
77
|
* storage feature they need is advertised by the target runtime.
|
|
75
78
|
*/
|
|
76
79
|
export function normalizeAppStoreStorageConfig(configArg, optionsArg, labelArg = defaultLabel) {
|
|
@@ -82,7 +85,8 @@ export function normalizeAppStoreStorageConfig(configArg, optionsArg, labelArg =
|
|
|
82
85
|
for (const request of explicitRequests)
|
|
83
86
|
requestOrigins[request.id] = 'explicit';
|
|
84
87
|
const declaredExclusions = configArg.backupExclusions ?? [];
|
|
85
|
-
const
|
|
88
|
+
const declaredDatabaseExclusions = configArg.databaseBackupExclusions ?? [];
|
|
89
|
+
const requiredFeatureIds = deriveRequiredFeatureIds(explicitRequests, declaredExclusions, declaredDatabaseExclusions);
|
|
86
90
|
const legacyVolumes = normalizeVolumes(configArg.volumes);
|
|
87
91
|
if (legacyVolumes.length > 0) {
|
|
88
92
|
storageClasses[legacyFilesystemClassKey] = {
|
|
@@ -148,6 +152,14 @@ export function normalizeAppStoreStorageConfig(configArg, optionsArg, labelArg =
|
|
|
148
152
|
reason: exclusion.reason,
|
|
149
153
|
};
|
|
150
154
|
}
|
|
155
|
+
// The contract accepted every entry: it names a database the version provisions, once.
|
|
156
|
+
const databaseBackupExclusions = {};
|
|
157
|
+
for (const exclusion of declaredDatabaseExclusions) {
|
|
158
|
+
databaseBackupExclusions[exclusion.database] = {
|
|
159
|
+
collections: [...exclusion.collections],
|
|
160
|
+
reason: exclusion.reason,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
151
163
|
const supportedFeatures = new Set(optionsArg.supportedFeatures);
|
|
152
164
|
const unsupportedFeatures = requiredFeatureIds.filter((featureIdArg) => !supportedFeatures.has(featureIdArg));
|
|
153
165
|
if (unsupportedFeatures.length > 0) {
|
|
@@ -159,7 +171,8 @@ export function normalizeAppStoreStorageConfig(configArg, optionsArg, labelArg =
|
|
|
159
171
|
requestOrigins,
|
|
160
172
|
requiredFeatureIds,
|
|
161
173
|
backupExclusions,
|
|
174
|
+
databaseBackupExclusions,
|
|
162
175
|
...(legacyCompatibility ? { legacyCompatibility } : {}),
|
|
163
176
|
};
|
|
164
177
|
}
|
|
165
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
178
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmFnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX2NsaWVudC9zdG9yYWdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBY3hDLE1BQU0saUJBQWlCLEdBQUcsT0FBTyxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FBQyx5QkFBeUIsQ0FBQztBQUN6RixNQUFNLHdCQUF3QixHQUFHLGtCQUFrQixDQUFDO0FBQ3BELE1BQU0sMkJBQTJCLEdBQUcscUJBQXFCLENBQUM7QUFDMUQsTUFBTSw0QkFBNEIsR0FBRyxXQUFXLENBQUM7QUFDakQsTUFBTSx1QkFBdUIsR0FBRyx3QkFBd0IsQ0FBQztBQUN6RCxNQUFNLFlBQVksR0FBRyxpQkFBaUIsQ0FBQztBQUV2Qzs7O0dBR0c7QUFDSCxNQUFNLGtCQUFrQixHQUFHLENBQUMsU0FBZ0MsRUFBRSxRQUFnQixFQUFRLEVBQUU7SUFDdEYsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FBQyw4QkFBOEIsQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDeEcsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUM7UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztBQUM1RCxDQUFDLENBQUM7QUFFRixNQUFNLGdCQUFnQixHQUFHLENBQUMsVUFBNEMsRUFBYSxFQUFFLENBQ25GLENBQUMsVUFBVSxJQUFJLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLFNBQVMsRUFBVyxFQUFFO0lBQzVDLE1BQU0sTUFBTSxHQUFHLE9BQU8sU0FBUyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUNwRixPQUFPO1FBQ0wsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUMzRCxTQUFTLEVBQUUsTUFBTSxDQUFDLFNBQVM7UUFDM0IsR0FBRyxDQUFDLE1BQU0sQ0FBQyxRQUFRLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUN2RSxHQUFHLENBQUMsTUFBTSxDQUFDLE1BQU0sS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDO0tBQ2xFLENBQUM7QUFDSixDQUFDLENBQUMsQ0FBQztBQUVMLDRGQUE0RjtBQUM1RixNQUFNLDJCQUEyQixHQUFHLENBQUMsU0FBa0IsRUFBRSxRQUFnQixFQUFFLFFBQWdCLEVBQVUsRUFBRTtJQUNyRyxNQUFNLFVBQVUsR0FBRyxTQUFTLENBQUMsSUFBSSxJQUFJLFNBQVMsQ0FBQyxTQUFTO1NBQ3JELFdBQVcsRUFBRTtTQUNiLEtBQUssQ0FBQyxHQUFHLENBQUM7U0FDVixNQUFNLENBQUMsT0FBTyxDQUFDO1NBQ2YsSUFBSSxDQUFDLEdBQUcsQ0FBQztTQUNULE9BQU8sQ0FBQyxjQUFjLEVBQUUsR0FBRyxDQUFDO1NBQzVCLE9BQU8sQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDM0IsTUFBTSxTQUFTLEdBQUcsaUJBQWlCLFVBQVUsRUFBRSxDQUFDO0lBQ2hELElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztRQUM1RCxNQUFNLElBQUksS0FBSyxDQUNiLFdBQVcsUUFBUSxZQUFZLFFBQVEsd0VBQXdFLENBQ2hILENBQUM7SUFDSixDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQyxDQUFDO0FBRUYsOEdBQThHO0FBQzlHLE1BQU0sd0JBQXdCLEdBQUcsQ0FDL0IsbUJBQXNDLEVBQ3RDLG1CQUFnRCxFQUNoRCwyQkFBZ0UsRUFDM0MsRUFBRTtJQUN2QixNQUFNLFFBQVEsR0FBd0IsRUFBRSxDQUFDO0lBQ3pDLElBQUksbUJBQW1CLENBQUMsTUFBTSxHQUFHLENBQUM7UUFBRSxRQUFRLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQzlFLElBQUksMkJBQTJCLENBQUMsTUFBTSxHQUFHLENBQUM7UUFBRSxRQUFRLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLHdCQUF3QixDQUFDLENBQUM7SUFDdEcsSUFBSSxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEtBQUssWUFBWSxDQUFDLEVBQUUsQ0FBQztRQUMvRSxRQUFRLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFDRCxJQUFJLG1CQUFtQixDQUFDLElBQUksQ0FBQyxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksS0FBSyxlQUFlLENBQUMsRUFBRSxDQUFDO1FBQ2xGLFFBQVEsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUNELElBQUksbUJBQW1CLENBQUMsTUFBTSxHQUFHLENBQUM7UUFBRSxRQUFRLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLDZCQUE2QixDQUFDLENBQUM7SUFDbkcsSUFBSSxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDLENBQzNDLFVBQVUsQ0FBQyxJQUFJLEtBQUssZUFBZSxJQUFJLFVBQVUsQ0FBQyxRQUFRLENBQUMsSUFBSSxLQUFLLE1BQU0sQ0FDM0UsQ0FBQyxFQUFFLENBQUM7UUFDSCxRQUFRLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLENBQUM7SUFDckQsQ0FBQztJQUNELE9BQU8sUUFBUSxDQUFDO0FBQ2xCLENBQUMsQ0FBQztBQUVGOzs7R0FHRztBQUNILE1BQU0sVUFBVSx3QkFBd0IsQ0FDdEMsU0FBZ0MsRUFDaEMsUUFBUSxHQUFHLFlBQVk7SUFFdkIsa0JBQWtCLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQ3hDLE9BQU8sZ0JBQWdCLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQzdDLENBQUM7QUFFRDs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLFVBQVUsOEJBQThCLENBQzVDLFNBQWdDLEVBQ2hDLFVBQWdELEVBQ2hELFFBQVEsR0FBRyxZQUFZO0lBRXZCLGtCQUFrQixDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQztJQUV4QyxNQUFNLGNBQWMsR0FBRyxlQUFlLENBQUMsU0FBUyxDQUFDLGNBQWMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUN2RSxNQUFNLGdCQUFnQixHQUFHLGVBQWUsQ0FBQyxTQUFTLENBQUMsZUFBZSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzFFLE1BQU0sZUFBZSxHQUFzQixDQUFDLEdBQUcsZ0JBQWdCLENBQUMsQ0FBQztJQUNqRSxNQUFNLGNBQWMsR0FBa0QsRUFBRSxDQUFDO0lBQ3pFLEtBQUssTUFBTSxPQUFPLElBQUksZ0JBQWdCO1FBQUUsY0FBYyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsR0FBRyxVQUFVLENBQUM7SUFDaEYsTUFBTSxrQkFBa0IsR0FBRyxTQUFTLENBQUMsZ0JBQWdCLElBQUksRUFBRSxDQUFDO0lBQzVELE1BQU0sMEJBQTBCLEdBQUcsU0FBUyxDQUFDLHdCQUF3QixJQUFJLEVBQUUsQ0FBQztJQUM1RSxNQUFNLGtCQUFrQixHQUFHLHdCQUF3QixDQUFDLGdCQUFnQixFQUFFLGtCQUFrQixFQUFFLDBCQUEwQixDQUFDLENBQUM7SUFFdEgsTUFBTSxhQUFhLEdBQUcsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzFELElBQUksYUFBYSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUM3QixjQUFjLENBQUMsd0JBQXdCLENBQUMsR0FBRztZQUN6QyxJQUFJLEVBQUUsWUFBWTtZQUNsQixPQUFPLEVBQUUsU0FBUztZQUNsQixRQUFRLEVBQUU7Z0JBQ1IsVUFBVSxFQUFFLFlBQVk7Z0JBQ3hCLEdBQUcsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FBQyxTQUFTLENBQUMsTUFBTSxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2FBQzNGO1NBQ0YsQ0FBQztJQUNKLENBQUM7SUFDRCxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUMsU0FBUyxFQUFFLFFBQVEsRUFBRSxFQUFFO1FBQzVDLE1BQU0sU0FBUyxHQUFHLDJCQUEyQixDQUFDLFNBQVMsRUFBRSxRQUFRLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDN0UsZUFBZSxDQUFDLElBQUksQ0FBQztZQUNuQixFQUFFLEVBQUUsU0FBUztZQUNiLElBQUksRUFBRSxZQUFZO1lBQ2xCLFlBQVksRUFBRSx3QkFBd0I7WUFDdEMsU0FBUyxFQUFFLFNBQVMsQ0FBQyxTQUFTO1lBQzlCLFVBQVUsRUFBRSxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLGVBQWU7WUFDakUsYUFBYSxFQUFFLFFBQVE7WUFDdkIsR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLEtBQUssS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsVUFBVSxFQUFFLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxFQUFFLENBQUM7U0FDOUUsQ0FBQyxDQUFDO1FBQ0gsY0FBYyxDQUFDLFNBQVMsQ0FBQyxHQUFHLGNBQWMsQ0FBQztJQUM3QyxDQUFDLENBQUMsQ0FBQztJQUVILElBQUksbUJBQTRFLENBQUM7SUFDakYsSUFBSSxTQUFTLENBQUMsb0JBQW9CLEVBQUUsRUFBRSxFQUFFLENBQUM7UUFDdkMsY0FBYyxDQUFDLDJCQUEyQixDQUFDLEdBQUc7WUFDNUMsSUFBSSxFQUFFLGVBQWU7WUFDckIsT0FBTyxFQUFFLFNBQVM7WUFDbEIsUUFBUSxFQUFFLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRTtTQUN2QyxDQUFDO1FBQ0YsZUFBZSxDQUFDLElBQUksQ0FBQztZQUNuQixFQUFFLEVBQUUsNEJBQTRCO1lBQ2hDLElBQUksRUFBRSxlQUFlO1lBQ3JCLFlBQVksRUFBRSwyQkFBMkI7WUFDekMsVUFBVSxFQUFFLFdBQVc7WUFDdkIsYUFBYSxFQUFFLFFBQVE7WUFDdkIsUUFBUSxFQUFFO2dCQUNSLElBQUksRUFBRSxzQkFBc0I7Z0JBQzVCLElBQUksRUFBRTtvQkFDSixRQUFRLEVBQUUsYUFBYTtvQkFDdkIsTUFBTSxFQUFFLFdBQVc7b0JBQ25CLE1BQU0sRUFBRSxXQUFXO29CQUNuQixXQUFXLEVBQUUsZUFBZTtvQkFDNUIsZUFBZSxFQUFFLGVBQWU7aUJBQ2pDO2FBQ0Y7U0FDRixDQUFDLENBQUM7UUFDSCxjQUFjLENBQUMsNEJBQTRCLENBQUMsR0FBRyxVQUFVLENBQUM7UUFDMUQsbUJBQW1CLEdBQUc7WUFDcEIsYUFBYSxFQUFFO2dCQUNiLFNBQVMsRUFBRSw0QkFBNEI7Z0JBQ3ZDLGVBQWUsRUFBRSx1QkFBdUI7Z0JBQ3hDLG9CQUFvQixFQUFFLDBCQUEwQjthQUNqRDtTQUNGLENBQUM7SUFDSixDQUFDO0lBRUQsd0dBQXdHO0lBQ3hHLE1BQU0sZ0JBQWdCLEdBQXlELEVBQUUsQ0FBQztJQUNsRixLQUFLLE1BQU0sU0FBUyxJQUFJLGtCQUFrQixFQUFFLENBQUM7UUFDM0MsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLGNBQWMsQ0FBQyxHQUFHO1lBQzNDLFdBQVcsRUFBRSxDQUFDLEdBQUcsU0FBUyxDQUFDLFdBQVcsQ0FBQztZQUN2QyxNQUFNLEVBQUUsU0FBUyxDQUFDLE1BQU07U0FDekIsQ0FBQztJQUNKLENBQUM7SUFFRCx1RkFBdUY7SUFDdkYsTUFBTSx3QkFBd0IsR0FBaUUsRUFBRSxDQUFDO0lBQ2xHLEtBQUssTUFBTSxTQUFTLElBQUksMEJBQTBCLEVBQUUsQ0FBQztRQUNuRCx3QkFBd0IsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLEdBQUc7WUFDN0MsV0FBVyxFQUFFLENBQUMsR0FBRyxTQUFTLENBQUMsV0FBVyxDQUFDO1lBQ3ZDLE1BQU0sRUFBRSxTQUFTLENBQUMsTUFBTTtTQUN6QixDQUFDO0lBQ0osQ0FBQztJQUVELE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLENBQUMsVUFBVSxDQUFDLGlCQUFpQixDQUFDLENBQUM7SUFDaEUsTUFBTSxtQkFBbUIsR0FBRyxrQkFBa0IsQ0FBQyxNQUFNLENBQUMsQ0FBQyxZQUFZLEVBQUUsRUFBRSxDQUFDLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7SUFDOUcsSUFBSSxtQkFBbUIsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDbkMsTUFBTSxJQUFJLEtBQUssQ0FBQyxlQUFlLFFBQVEsc0JBQXNCLG1CQUFtQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDakcsQ0FBQztJQUVELE9BQU87UUFDTCxjQUFjO1FBQ2QsZUFBZTtRQUNmLGNBQWM7UUFDZCxrQkFBa0I7UUFDbEIsZ0JBQWdCO1FBQ2hCLHdCQUF3QjtRQUN4QixHQUFHLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0tBQ3hELENBQUM7QUFDSixDQUFDIn0=
|
|
@@ -17,6 +17,11 @@ export interface IAppStoreStorageLegacyCompatibility {
|
|
|
17
17
|
* `backupExclusions` entry for that request and the operator-readable reason.
|
|
18
18
|
*/
|
|
19
19
|
export type TAppStoreNormalizedBackupExclusion = Omit<plugins.servezoneInterfaces.appstore.IAppStoreBackupExclusion, 'storageRequest'>;
|
|
20
|
+
/**
|
|
21
|
+
* The collections of one database a backup exports without their documents: the collections of a
|
|
22
|
+
* version's `databaseBackupExclusions` entry for that database and the operator-readable reason.
|
|
23
|
+
*/
|
|
24
|
+
export type TAppStoreNormalizedDatabaseBackupExclusion = Omit<plugins.servezoneInterfaces.appstore.IAppStoreDatabaseBackupExclusion, 'database'>;
|
|
20
25
|
/** The canonical storage handoff a fulfillment adapter receives for one version config. */
|
|
21
26
|
export interface INormalizedAppStoreStorageConfig {
|
|
22
27
|
storageClasses: Record<string, plugins.servezoneInterfaces.appstore.IAppStoreStorageClass>;
|
|
@@ -29,6 +34,12 @@ export interface INormalizedAppStoreStorageConfig {
|
|
|
29
34
|
* objects under these key prefixes out; a restore neither brings them back nor removes them.
|
|
30
35
|
*/
|
|
31
36
|
backupExclusions: Record<string, TAppStoreNormalizedBackupExclusion>;
|
|
37
|
+
/**
|
|
38
|
+
* Declared observability-only collections by database (`mongodb` for
|
|
39
|
+
* `platformRequirements.mongodb`), empty when the version declares none. A backup exports these
|
|
40
|
+
* collections with their indexes and without their documents; a restore brings them back empty.
|
|
41
|
+
*/
|
|
42
|
+
databaseBackupExclusions: Partial<Record<plugins.servezoneInterfaces.appstore.TAppStoreBackupExclusionDatabase, TAppStoreNormalizedDatabaseBackupExclusion>>;
|
|
32
43
|
legacyCompatibility?: IAppStoreStorageLegacyCompatibility;
|
|
33
44
|
}
|
|
34
45
|
export interface IAppStoreStorageNormalizationOptions {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/appstore",
|
|
3
|
-
"version": "33.
|
|
3
|
+
"version": "33.12.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "serve.zone App Store: the signed runtime catalog and its consumer loader.",
|
|
6
6
|
"type": "module",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"author": "Task Venture Capital GmbH",
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@serve.zone/interfaces": "32.
|
|
15
|
+
"@serve.zone/interfaces": "32.58.0",
|
|
16
16
|
"modern-tar": "0.8.5"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
package/readme.md
CHANGED
|
@@ -37,7 +37,7 @@ The catalog lists 19 apps. Every image is pinned to the digest of its multi-plat
|
|
|
37
37
|
| App ID | Name | App Store category | Latest | Image | Port |
|
|
38
38
|
| --- | --- | --- | --- | --- | --- |
|
|
39
39
|
| `adminer` | Adminer | Dev Tools | `1.0.0` | `adminer@sha256:74f29c416e148b98305e84446a18db7cd2c1038264dec464359d36774ef080ce` | `8080` |
|
|
40
|
-
| `cloudly` | Cloudly | Dev Tools | `1.
|
|
40
|
+
| `cloudly` | Cloudly | Dev Tools | `1.30.0` | `code.foss.global/serve.zone/cloudly@sha256:cd0bfc82575d9a4a9d0808e19ec61b0bf188d83729d66195afbf5f373164f16e` | `3000` |
|
|
41
41
|
| `ghost` | Ghost | CMS | `1.0.0` | `ghost@sha256:9482099b1c8700764dc3d38c293e09c16d70f8bacc14af589c47d90b4bebd017` | `2368` |
|
|
42
42
|
| `gitea` | Gitea | Dev Tools | `1.0.0` | `gitea/gitea@sha256:a9dc25679ebdd33356242d2e0667cc220043745e8339922879f187c20329a42e` | `3000` |
|
|
43
43
|
| `grafana` | Grafana | Monitoring | `1.0.0` | `grafana/grafana@sha256:b28bae15e219c998fb0e0424ed724930cc61b1f61fb404d47c862f9a23f9e572` | `3000` |
|
|
@@ -68,7 +68,7 @@ Some templates ask Onebox to provision local platform services and inject connec
|
|
|
68
68
|
|
|
69
69
|
| App ID | Requirements | Notes |
|
|
70
70
|
| --- | --- | --- |
|
|
71
|
-
| `cloudly` | MongoDB, S3 | Versions `1.29.4`, `1.29.3`, `1.29.2`, `1.29.1`, `1.29.0`, `1.28.6` and `1.28.5` declare Cloudly's workload logs non-backup data: `backupExclusions` names the key prefix `runtime-logs/` of the `legacy-s3` bucket, and `requiresFeatures` names `storage.object-storage.backup-exclusions`, so a host that does not fulfil it lists them and refuses them as targets. A backup on a host that fulfils it leaves those objects out, and a restore brings back Cloudly's state, not its past workload logs. Version `1.29.4` is `1.29.3` with the Cloudly `33.20.4` image and is the upgrade target of `1.29.3`, `1.29.2`, `1.29.1`, `1.29.0`, `1.28.6`, `1.28.5` and `1.28.3`. Cloudly `33.20.4` keeps the runtime log sweep bounded by its own work: its reads no longer scan the whole runtime log ledger, each sweep step commits at most 16 rows under a 15 s deadline instead of retrying past the database's transaction lifetime, and each step first lets critical node requests (such as the managed VPN credential renewal) through, so a large ledger no longer stalls node credentials. It adds no data step, no collection and no index and keeps the data version `0.31.0`, so an upgrade from `1.29.3` or `1.29.2` runs none. Version `1.29.3` is `1.29.2` with the Cloudly `33.20.3` image and is the upgrade target of `1.29.2`, `1.29.1`, `1.29.0`, `1.28.6`, `1.28.5` and `1.28.3`. Cloudly `33.20.3` lets a service that already holds a Corestore binding of a capability adopt its own legacy resource of that capability into that binding: Corestore `34.1.4` and later refuse a binding beside an unauthorized legacy resource (`legacy-adoption-pending`), and `adoptCorestoreLegacyResources` refused such a service `binding-exists`, so the service stayed down; now the adoption is written onto the matching enabled canonical binding in the adoption's transaction, and every other binding still refuses `binding-exists`. It adds no data step and no collection and keeps the data version `0.31.0`, so an upgrade from `1.29.2` runs none; an upgrade from `1.29.1` or earlier also takes the changes of Cloudly `33.20.2` below. Version `1.29.2` is `1.29.1` with the Cloudly `33.20.2` image and is the upgrade target of `1.29.1`, `1.29.0`, `1.28.6`, `1.28.5` and `1.28.3`. Cloudly `33.20.2` keeps its registry storage bounded: a storage retention runs 3 minutes after start, then daily, prunes releases no plan, running tag, deployment operation or unfinished rollout needs beyond each service's 3 most recently promoted accepted releases and the last 7 days, and deletes manifests and blobs nothing reaches after 24 hours, in time-bounded holds of the registry mutation lock; the per-push prune is gone, and a service deletion deletes only its own repository's manifests and tags. A rollout of what a replica already runs keeps it running, and gateway, mail and CoreMail status reported under a service lifecycle lease is kept, one record per observer in `cloudly_service_reconciliation_stages`. Its startup runs the forward-only data step `service-reconciliation-observations` from `0.30.0` to `0.31.0`; every Cloudly before `33.20.2` refuses `0.31.0`, so a return to `1.29.1` or earlier means restoring the backup taken before the upgrade, and registry objects the retention deleted do not come back with it. An upgrade from `1.29.0` or earlier also takes the changes of Cloudly `33.20.1` below. Version `1.29.1` is `1.29.0` with the Cloudly `33.20.1` image and is the upgrade target of `1.29.0`, `1.28.6`, `1.28.5` and `1.28.3`. Cloudly `33.20.1` no longer blanks a cluster's ingress routes when it restarts: its first table of a cluster is the full one, never an empty revocation, a node that only registers again keeps its routes because a route endpoint is a node instance's statement under its credential generation (`nodeInstanceNonce`), unreadable route sources keep the relay's table for at most 180 s after a start, and a revocation withdraws exactly the revoked routes. A Pallet registration Cloudly cannot serve for a reason of its own is refused as momentary (`controller-unavailable`, `controller-deadline`; @serve.zone/interfaces `32.57.0`), so the node registers again instead of ending its session, and a credential check whose store cannot be read fails closed. It adds no data step and no collection and keeps the data version `0.30.0`, so an upgrade from `1.29.0` runs none; an upgrade from `1.28.6` or earlier also takes the changes of Cloudly `33.20.0` below. Version `1.29.0` is `1.28.6` with the Cloudly `33.20.0` image and is the upgrade target of `1.28.6` and `1.28.5`. Cloudly `33.20.0` serves a non-public hostname from a node's cluster ingress to dcrouter alone once the setting `dcrouterForwardSourceAddresses` names dcrouter's forwarding sources (the cluster's Coreflow and CoreTraffic must run releases on `@serve.zone/interfaces` `32.55.0` or later), writes workload log payloads as one segment object per capture every few minutes, keeping a committed batch inline in the new collection `cloudly_runtime_log_inline_payloads` until its segment is written, and lets a service whose retained rollout plan cannot run be redeployed (`RECOVERY_REDEPLOY_OVER_FAILED_RUNTIME`). It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.28.6` runs none; its first start creates that collection, which after an unclean stop of the previous Cloudly fails with `EFENCE_BUSY` for up to 30 minutes on Corestore `32.2.4`. Cloudly `33.19.2` and earlier refuse the log ledger rows `33.20.0` writes, so a return to an earlier template needs the backup taken before the upgrade. `1.28.6` is `1.28.4` (Cloudly `33.19.2`) with that declaration; `1.28.5` is `1.28.3` (Cloudly `33.19.1`) with it, so a host on `1.28.3` moves its template without moving Cloudly. `1.28.5` sorts above `1.28.4` but runs the older image: from `1.28.4`, upgrade to `1.28.6`. Version `1.28.4` pins Cloudly `33.19.2` and keeps the `1.28.3` runtime contract, including the Onebox `33.8.0` minimum: its platform Corestore `32.2.4` runs NoSQLDB `10.6.1` with the D3 write-conflict fix, and on an earlier engine a node's network plan loses every commit to a `WriteConflict`, so Cloudly `33.15` must not run on it. Cloudly `33.19.2` is a fixes-only patch over `33.19.1`: a greenfield deployment, a legacy import's successor included, proves its namespace blank on the nodes of the Pallet clusters only, so a fleet that still keeps a Coreflow-owned Swarm cluster without a relay can reserve one, while a Pallet node whose inventory cannot be read still blocks; a greenfield rollout without a Pallet runtime spec is refused `GREENFIELD_PALLET_RUNTIME_SPEC_REQUIRED` instead of falling back to Coreflow placement (state the spec with `setServiceRuntimeSpec`, then promote again); and a node's network compilation proves each workload lease and each authority or eligibility proof once per transaction. It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.28.3` runs none. The `1.28.4` changelog carries the changes of `33.19.2` and of `33.18.3` to `33.19.1` and the earlier releases' upgrade path only, because a catalog changelog is bounded at 65536 characters; `1.28.2`'s carries the rest. From `1.28.2` or earlier, Cloudly `33.19.1`'s changes apply too: Cloudly `33.19.1` carries `33.18.3` to `33.19.1`: a node's log report costs one append transaction that queues the batch with its payload in the new collection `cloudly_runtime_log_queue`, and a background drain writes the payloads and commits the ledger rows without holding the node's request or fencing the controller record (a full queue refuses a report with the retryable `log-intake-saturated`), object writes list nothing, workload logs are kept for at least 30 days, route verification reads the certificate each route presents on a connection of its own, and `readServiceDeploymentStatus` (interfaces `32.54.0`) reads a deployment operation without changing it. Its first start creates `cloudly_runtime_log_queue`; on NoSQLDB `10.6.1` that creation waits at most 5 s for the open transactions on the Cloudly database, and a transaction an earlier Cloudly left open by exiting before its stop completed holds the database until its session expires 30 minutes later, so after an unclean stop of the previous Cloudly start the upgrade no earlier than 30 minutes after that exit. It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.28.2`, `1.28.1` or `1.28.0` runs none. From `1.28.1` or earlier, Cloudly `33.18.2`'s fixes apply too: Cloudly `33.18.2` is a fixes-only patch over `33.18.1`: a node's network configuration advances only once the node's application receipt names the projection it holds, so a node no longer receives a new version while it still applies the last one; upgrade every node to Pallet `36.3.0` or later (interfaces `32.50.0` or later) first, because a node that sends no receipt keeps the projection it holds and receives no successor. It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.28.1` or `1.28.0` runs none. From `1.28.0` or earlier, Cloudly `33.18.1`'s fixes apply too: Cloudly `33.18.1` is a fixes-only patch over `33.18.0`: a node's log batches append without its node fences, so they no longer miss their budget against the node's producer passes, and the deployment preflight answers within 25 s, past which it answers the retryable `DEPLOYMENT_PREFLIGHT_UNAVAILABLE`. It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.28.0` runs none. From `1.27.0` or earlier, Cloudly `33.18.0`'s changes apply too: Cloudly `33.18.0` loads the App Store catalog at runtime instead of bundling it, from the registries of its `appStoreCatalogRegistries` setting (empty means the npm registry), so a Cloudly template update needs an appstore release only; it ignores `APPSTORE_URL`, whose remote source is removed, and while no verified catalog is active it refuses App Store install details, upgrade preview and upgrade with `APPSTORE_CATALOG_UNAVAILABLE`. It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.27.0` runs none. From `1.26.0` or earlier, Cloudly `33.17.3`'s changes apply too: Cloudly `33.17.3` moves the data version from `0.28.0` to `0.30.0`: every upgrade from `1.26.0` or earlier runs the forward-only data steps `route-claim-absent-owners` to `0.29.0` (the stored nulls of absent owners are removed from every route claim) and `bounded-runtime-history` to `0.30.0` (every archived deployment record is deleted, the index `archiveState_1` is dropped and the accepted Spark observations are pruned to the rows the census reads), and every Cloudly before `33.17.3` refuses the `0.30.0` data. Runtime history is bounded from then on: a Coreflow report deletes the deployment records it no longer names instead of archiving them, `getDeployments` answers the current records whether or not it is asked to `includeArchived`, `deploymentArchiveRetentionDays` is accepted but has no effect, and the dashboard no longer offers archived deployments. A deployment preflight no longer fails `DEPLOYMENT_PREFLIGHT_UNAVAILABLE` on a route claim that stores an absent owner as `null`, and the deployment and adoption preflights log the cause of an unexpected failure. A node's requests no longer contend on the controller fence and share a 6 s budget below the cluster relay's 8 s forward deadline, a request out of budget is refused `controller-deadline`, which the node retries, and a node deletion refuses what a racing write bound to the node. Cloudly consumes the projection application receipts of Pallet `36.3.0`, so a flow a node's projection withdrew is granted again on the same leases once the node reports that it applied the withdrawal; an earlier Pallet keeps every withdrawal carried. Cloudly `33.17.2` starts a database below data version `0.25.0` that holds a runtime slot: the data step `runtime-log-attempts` read through models startup has not prepared and was refused, and now reads and writes through the driver; an install whose first start failed there stayed at data version `0.24.0` and upgrades to `1.28.4` normally. Cloudly `33.17.1` fixes the first start of `33.17.0`, which crash-looped on a database holding Pallet-era nodes: its first data step `cluster-node-hostname-intent` stated each intent through the node and cluster models, which startup has not prepared while it runs the data steps, and was refused before its first write; the step now reads and writes the stored node and cluster documents directly, like the other data steps. The migration ledger does not advance past a failed step, so an install on `1.25.0` whose first start failed stayed unchanged at data version `0.25.0` and upgrades to `1.28.4` normally. Cloudly `33.17.1` keeps the data steps of `33.17.0`, which move the data version from `0.25.0` to `0.28.0`: every upgrade from `1.24.0` or earlier, and from `1.25.0` whose first start failed, runs the forward-only data steps `cluster-node-hostname-intent` to `0.26.0` (every Pallet-era node gets the intent of the hostname it reports, and nothing is renamed), `deployment-operation-route-evidence` to `0.27.0` (a routeless deployment operation that succeeded records its empty route evidence) and `service-secret-target-predecessor` to `0.28.0` (a secret target state an earlier service deletion left behind is removed) before the `0.28.0` to `0.30.0` steps. The template stays marked breaking, migration-required, backup-before-upgrade and manual-review, because hosts read the flags from the target version alone and every upgrade from `1.26.0` or earlier runs the `0.28.0` to `0.30.0` steps, after the `0.25.0` to `0.28.0` steps from `1.24.0` or earlier; every upgrade from `1.19.0` or earlier runs the forward-only data step `runtime-log-attempts` from `0.24.0` to `0.25.0` before them (the log record of every slot's current attempt is written), and every upgrade from `1.15.0` or earlier runs `corestore-control-token-state` from `0.23.0` to `0.24.0` before that (a Corestore control token stored earlier is recorded as revision 1); every Cloudly before `33.17.3` refuses the migrated data: stop Cloudly and take a verified backup first, as rollback is full-restore-only. From `1.24.0` or earlier, Cloudly `33.17.0`'s changes apply too: Cloudly `33.17.0` lets a legacy import reserve: it takes over every route claim its legacy service holds in the import's organization, the cluster ingress keeps serving each hostname for the legacy service until the import's service declares it, and cleanup or rollback before promotion hands the claims back while the legacy service exists; `ROUTE_UNAVAILABLE` and `DEPLOYMENT_ROUTE_COLLISION` name each conflicting hostname and the services holding it. It owns the hostname a node should carry (interfaces `32.52.0`, `cloudly-worker-<name>` by default): `setClusterNodeHostname` renames a Pallet-era node and `resetClusterNodeHostname` sets the default, Spark reads the intent at `POST /spark/nodes/host`, and a Pallet service's gateway routes and inbound mail follow the node its runtime spec pins by id, so a rename moves no traffic. A routeless deployment records its empty route evidence when it succeeds; deleting a cluster's designated ingress service is refused `SERVICE_IS_CLUSTER_INGRESS`, `designateClusterIngress` recovers a designation whose bearer is no longer active, and a deleted service's secret target state goes with its row, so a service recreated under its id writes its secrets again. From `1.23.0` or earlier, Cloudly `33.16.0`'s changes apply too: Cloudly `33.16.0` captures a legacy service's runtime attestation from an administrator's host observation, without a Coreflow session: `captureLegacyServiceRuntimeAttestation` takes `hostObservation`, the legacy Swarm service's spec image and mode and, per replica, its node, Swarm task, container, image store, image id, `RepoDigests`, state and health, read at most 15 minutes before the capture; Cloudly judges it against the one root every replica runs, which it requires whole in its registry and which the legacy tag need not still name, and refuses incomplete evidence without storing anything. The registry prune keeps an attested root until a release's `cloudly-legacy-import` evidence names the attestation or the legacy service is retired. From `1.22.0` or earlier, Cloudly `33.15.3`'s changes apply too: Cloudly `33.15.3` serves a promoted route: a route's identity carried the service's runtime write fence `runtimeAuthorityFenceGeneration`, which every runtime write of the service advances, in a Pallet-born cluster the route join's own workload lease read included, so the route plan read after the cluster ingress's certificate fetch never matched the one before it and every refresh that fetched a certificate withdrew the cluster's whole route table; a route's identity now carries only the versions that change with its authority (the route intent, the runtime policy revision, and per endpoint its assignment, its node's session and its target), while a service being deleted or holding no runtime authority is still refused by the route join, so a revoked route is withdrawn as before, by name. A table withdrawn because its authority changed or its route sources could not be read names that withdrawal as the reason the cluster ingress serves none of its routes, so a failing route verification states it; that reason states facts about its own route only, because one table carries the routes of several organizations: a route whose authority changed names its changed field, every other route only that another route's authority (or the ingress designation) changed during the certificate fetch, and a source-read failure only that the route sources could not be read, its error staying in the operator log. From `1.21.0` or earlier, Cloudly `33.15.2`'s changes apply too: Cloudly `33.15.2` lets a promoted route reach CoreTraffic: a route's identity carries the route intent version `routeIntentGeneration` instead of the whole-document `routeStateGeneration`, so the status writes during the cluster ingress's certificate fetch no longer withdraw the cluster's route table, a plan change during the fetch withdraws it only when it revokes a route the table is about to serve, and the change is named by route and input. A cluster ingress flow that a compensated promotion withdrew and a later promotion wants again no longer stalls the node's network projection: Cloudly holds that flow back with its withdrawal carried and signs the rest of the node's network, while a workload or router grant wanted again while withdrawn withholds the node's projection as `withdrawn-grant-reissued` and any other contract refusal as `projection-rejected`, each naming what it holds; the cluster ingress leaves a route whose grant is held out by name, and a failed route verification states why no route is served for its hostname. A held flow is granted again only once the target or CoreTraffic runs under a new lease. From `1.20.0` or earlier, Cloudly `33.15.1`'s changes apply too: Cloudly `33.15.1` fixes the deployment preflight of an existing service Pallet runs: it read the current runtime from Coreflow alone and answered NO-GO (`CURRENT_RUNTIME_DIGEST_MISSING`, `RUNTIME_ATTESTATION_MISMATCH`) for a service whose rollout status was verified, and now reads the runs of the service's current plan, the evidence its deployment status reads, when Pallet executes that plan on every cluster it targets. It also lets a route promotion survive the status writes of its own gateway sync: the promotion fenced on `routeStateGeneration`, the service's whole-document version, which the route status its gateway sync records, Coreflow's reconciliation reports and its own verified-route status all advance, so every promotion failed `DEPLOYMENT_ROUTE_FENCE_CONFLICT` with no route evidence; it now fences on `routeIntentGeneration`, which only a write that changes the service's domains advances, so a concurrent route change, a change and back included, still fails it fenced, and a promotion interrupted under `33.15.0` is recovered as fenced (retryable), without a rollback. From `1.19.0` or earlier, Cloudly `33.15.0`'s changes apply too: Cloudly `33.15.0` keeps workload logs: a Pallet node's log batches are kept per attempt, their payloads in Cloudly's own bucket (the template's `S3_BUCKET`) under `runtime-logs/`, bounded to 8 MiB and 8192 batches per attempt and the newest 8 attempts per slot, each batch for its config's `logs.retentionMs`, and read with `getServiceRuntimeLogAttempts` and `getRuntimeAssignmentLogs` (interfaces `32.47.0`) or the service page's Logs tab. A Pallet-executed rollout that succeeded stays succeeded when a replica later fails or is replaced; a failing service runtime pass names its step, and a database error its code name and number. A launcher secret written before a service's first image plan no longer leaves the service refused `secrets-unsupported`, because every image plan is written with the service's secret deployment plan in one transaction. The designated cluster ingress runs with `SERVEZONE_CLUSTER_RELAY_ORIGIN` and `SERVEZONE_CLUSTER_NODE_ID`, which Cloudly writes into that workload's environment (a cluster without a valid relay refuses it `cluster-relay-missing`); the template sets neither. From `1.18.0` or earlier, Cloudly `33.14.1` and `33.14.2`'s changes apply too: Cloudly `33.14.2` completes a rollout Pallet executes: Cloudly reports on every rollout whose target clusters are all on Pallet, one reporter per cluster (`pallet/<clusterId>`) over the runs of exactly the current desire, so it moves to `succeeded` (or `ready-for-route`) once its runs are ready at the requested digest and fails when a run fails, and `getServiceDeploymentStatus` reads its runtime tasks from the runtime manager instead of Coreflow, so tsdeploy no longer times out on a Pallet-attached service. A launcher-secret run admitted after a Cloudly restart, reconnect or credential rotation gets its material again instead of waiting `secret-mount-uncertain` (`secret-recipient-inactive`) until its node process restarts, because a node's secret recipient is active for every session of the node process that enrolled it. A failing producer pass is logged under its producer's subject once per change of cause, with how long it ran and a value-free cause naming the failing step and check (such as `network-projection-rejected`), and its recovery once; a failed sweep the timer started is no longer dropped. From `1.17.0` or earlier, Cloudly `33.14.0`'s changes apply too: it gives a platform-owned service's published ports infrastructure-administrator authority: an administrator designates a platform organization's service (an organization id carrying `:`) with `designatePlatformOwnedService`, withdraws it with `undesignatePlatformOwnedService` and reads it with `getServicePlatformOwnership` (interfaces `32.45.0`), after which only a verified administrator reads and writes its published ports, any other organization id is refused `organization-not-platform`, and the designation, stored on the service document, is read inside the published-port request's one admitted operation; every organization service keeps the organization-owner rule. It also names and logs every assignment dispatch outcome under a closed, value-free reason, pushes a refused or unanswered push again on the next pass, and keeps a run its node refused (cause `refused`) instead of replacing it on the failure backoff until its input changes. From `1.16.0` or earlier, Cloudly `33.13.3` and `33.13.4`'s changes apply too: Cloudly `33.13.4` delivers a run's secret material through the WorkloadInit launcher again (Corestore's `MONGODB_URI` included), remembers a successful WorkloadInit approval verification per artifact, and names the check of every refused material request with a closed, value-free `reason`; since Cloudly `33.13.3` a node's session is pushed each assignment revision once instead of on every delivery pass. From `1.15.0` or earlier, Cloudly `33.11.0` to `33.13.2`'s changes apply too: they let an administrator set the Corestore control token without an exec into Cloudly's container (`setCorestoreControlToken`, interfaces `32.42.0`, with its revision and value-free receipts in the new collections `cloudly_corestore_control_token_state` and `cloudly_corestore_control_token_receipts`, and `servezone corestore set-control-token`), keep every managed VPN member across a protected-authority step that only adds, retire a node and its egress owner (`retireClusterNode`, interfaces `32.43.0`), ask a retiring node on interfaces `32.44.0` to withdraw its allocation protection so that a stopped or offline node no longer blocks allocation, and keep a stopping workload's lease in its node's projection until the node's stop receipt is accepted. From `1.14.0` or earlier, Cloudly `33.10.0`'s changes apply too: it lets an operator obtain a cluster relay's bearer without an exec into Cloudly's process: `exportClusterRelayAuthorization` (interfaces `32.41.0`) requires a freshly signed-in administrator, proves the delivered credential version against the hash its generation issued, commits an audit receipt fenced at that generation before it answers the bearer, and never logs it; the receipts live in the new collection `cloudly_cluster_relay_authorization_exports`, installed at startup and listed by `getClusterRelayAuthorizationExports`, and `servezone relay export-authorization` (`@serve.zone/cli` `33.10.0`) writes the bearer as a new `0600` file. From `1.14.0` or earlier, Cloudly `33.9.0`'s changes apply too: a node's managed VPN member is kept across its network projection changes instead of being withdrawn from the hub network on every workload change (`bindGetRuntimeManagedVpnCredentialSuccessor`, interfaces `32.39.0`), the failure cause a node reports with a failed run (interfaces `32.40.0`) is stored and shown in the runtime assignment views and the dashboard's Cause column, and `deleteServiceById` answers success once a service is marked for deletion while another deleting service drains, naming its refusals `SERVICE_LIFECYCLE_BUSY`, `SERVICE_ORGANIZATION_AUTHORITY_REQUIRED` and `SERVICE_MANAGER_STOPPING` instead of an internal error. From `1.13.0` or earlier, Cloudly `33.8.3`'s fix applies too: it lets `deleteServiceById` retire a legacy service whose Corestore resources a successor adopted: its live Corestore bindings are tombstoned with the ordinary config push once every claimed legacy resource of each binding's capability has passed to the successor's live adopting binding, while a service without such a handover is still refused `CORESTORE_CLEANUP_TARGET_AUTHORITY_REQUIRED`. A legacy service whose runtime attestation a legacy-import operation still uses (`reserving`, `reserved`, `provisioning` or `awaiting-image`) is refused `SERVICE_LEGACY_IMPORT_PENDING` (retryable) and keeps running; import and deletion are serialized on the registry repository, so an import that starts after the check refuses as `LEGACY_IMPORT_SOURCE_MISMATCH`, and a deletion that proceeds deletes the attestations no release consumed. From `1.12.0` or earlier, Cloudly `33.8.2`'s fix applies too: Cloudly names why it refused a node's report: a refused `reportRuntimeNetworkProtection`, `reportRuntimeAssignmentTerminalReceipt`, `reportRuntimeAssignmentObservation` or `reportRuntimeAssignmentLogs` is logged as a warning once per node, report and distinct reason, and the node is answered with the unchanged error text plus `{ code: 'runtime-session-report-refused', reason }` as the error's data. From `1.11.0` or earlier, Cloudly `33.8.1`'s fix applies too: deploy before any Pallet that sends `discardedCaptures` or a `not-kept` log loss reaches a fleet node, since it accepts those log batch fields of interfaces `32.38.0`, which Cloudly `33.8.0` refuses on every retry, and still keeps no workload logs. From `1.10.0` or earlier, Cloudly `33.8.0`'s changes apply too: deploy before any Pallet that captures workload output reaches a fleet node, since Cloudly `33.8.0` answers a Pallet's `reportRuntimeAssignmentLogs` (interfaces `32.37.0`) with `not-kept`, which an earlier Cloudly leaves unhandled. Cloudly `33.8.0` lets a legacy `deployOnPush` service without deployable source move to a greenfield successor that runs the exact image it ran (`captureLegacyServiceRuntimeAttestation`, a `legacyImport` reservation under the capability `deployment:import-legacy`, and `importServiceDeploymentLegacyImage` in place of a push), judges promotion against the platforms of the service's rollout target, refuses a registry repository that belongs to another service as `REGISTRY_REPOSITORY_MISMATCH`, promotes and routes a service with a Corestore `database` or `objectstorage` binding again, and reads a node whose last report is older than 180 seconds as `offline`. Its web UI moved to path URLs and a regrouped menu, so bookmarks of the old UI locations open the not-found screen; server paths such as `/status` and the OIDC callback are unchanged. From `1.9.0` or earlier, Cloudly `33.7.0`'s changes apply: Cloudly `33.7.0` lets a fresh greenfield service take over a legacy service's Corestore database and bucket with their data and retention intact: an `adoptCorestoreLegacyResources` selection entry may name `successorServiceId` (interfaces `32.34.0`, carried out by Corestore `33.2.0` or later), the successor must share the owner's organization, be an immutable-deployment service, own no legacy resource of the capability and hold no other binding of it, and each legacy resource is adopted by one service only (`legacy-resource-adopted-elsewhere`, `successor-organization-mismatch`, `successor-not-greenfield`, `successor-owns-legacy-resource`). Since Cloudly `33.6.1`, a deployment reservation whose release tag the registry already holds is refused `DEPLOYMENT_RELEASE_TAG_OCCUPIED` instead of waiting for its image forever, an administrator's manifest push to a release tag a deployment operation reserved is refused `409 release-tag-reserved`, and a binding status report reaches its cluster relay only when it changed what the relay reads. From `1.8.0` or earlier, startup runs the forward-only data step `deployment-machine-user-grants` from `0.22.0` to `0.23.0`, which keeps on every deployment machine user only the grants a capability check reads and permanently removes the rest (such as the `{ serviceId, capabilities }` grants of Cloudly 11); stop Cloudly and take a verified backup first, as the way back is the pre-upgrade backup. Cloudly `33.6.0`'s changes apply too: Cloudly stops gracefully on SIGTERM and SIGINT within 8 seconds, below the 10-second stop grace of Docker and Swarm, so a stop or restart no longer leaves a database transaction held and the next start no longer crash-loops on `EFENCE_BUSY`; a process ended by SIGKILL still holds its database session until the server's session timeout. Every new service gets its own deployment namespace, so two services of one project reserve without `DEPLOYMENT_RESOURCE_COLLISION`, while a deployed service keeps its namespace, and `getDeploymentMachineUsers` lists users configured under Cloudly 11 again. From `1.7.0` or earlier, Cloudly `33.5.0`'s changes apply: Cloudly reserves a deployment only for a deployment machine user: `reserveServiceDeployment` refuses an administrator with `DEPLOYMENT_MACHINE_IDENTITY_REQUIRED`, including a deployer that signs in with a username and password (such as tsdeploy with `TSDEPLOY_CLOUDLY_USERNAME`/`TSDEPLOY_CLOUDLY_PASSWORD`), so before upgrading create a deployment machine user per deployer (`mutateDeploymentMachineUser`, or Security → Access → Deployment machine users in the dashboard), attach its grants with `configureServiceDeploymentMachineUser` and switch the deployer to its token; an existing `api` machine user with its grants already qualifies. A machine identity or registry bearer issued by an earlier release is refused until its holder exchanges its token again with `getIdentityByToken`, and removing a token now ends the sessions opened with it. Let no deployment run during the upgrade: a run in progress keeps its pre-upgrade identity and is refused for the rest of that run (rerun it, or resume it with `deploy-retry`). From `1.3.0` through `1.6.0`, startup runs the forward-only data step `runtime-session-protocol-offer` from `0.21.0` to `0.22.0`, which states `protocol: null` on every Pallet session row recorded before Cloudly stored its node's protocol offer; stop Cloudly and take a verified backup first, as rollback is full-restore-only. From `1.6.0`, Cloudly `33.4.0`'s changes apply: Cloudly `33.4.0` lets `setServicePublishedPorts` publish a contiguous range through `hostPortEnd` (at most 1024 ports in 64 entries per protocol) and same-port outbound through `symmetric: true`, which needs public egress (`published-port-symmetric-without-egress`) and owns its inside ports (`published-port-symmetric-inside-overlap`). New handoff leases translate outbound flows only into `49152-65535`, whose ports are refused `published-port-reserved`; a node keeps its `1024-65535` lease until its router is re-created. A policy write for a service being deleted is refused `service-deleting`. A projection carrying a range or a symmetric entry is withheld as `node-contract-behind` from a Pallet node whose session states a release below interfaces `32.31.0`, or an unrecorded one, so ranges and symmetric entries take effect on a node only once it runs a Pallet release built on interfaces `32.31.0` or later. From `1.5.0`, Cloudly `33.3.0`'s changes apply: Cloudly `33.3.0` lets an administrator set a service's network egress with `setServiceNetworkEgress` (public internet, and the platform endpoints of the protected authority), and refuses a workload whose database or object-storage binding sits on a node whose Corestore endpoint its network does not select as `database-endpoint-unselected` or `objectstorage-endpoint-unselected`; a replica already running keeps running while its target reports the refusal. From `1.4.0`, Cloudly `33.2.0`'s changes apply: Cloudly takes a dcrouter admin token once through `bootstrapExternalGatewayCredential` to provision its own gateway-client credential, starts a first enrollment with the gateway settings, and stages a gateway settings change the held credential cannot carry until the next bootstrap. From `1.2.1` or earlier, startup runs the forward-only migration from `0.20.0` to `0.21.0` before that step, dropping the relay certificate and DNS collections of Cloudly's retired Cloudflare and Let's Encrypt integration and logging every relay name whose A record Cloudly had written at Cloudflare, which the operator removes at the provider. dcrouter holds every cluster relay name's A record and certificate from then on, `SERVEZONE_SSLMODE=letsencrypt` is refused, and the typed API changes as the Cloudly 33.0.0 changelog lists. Stop Cloudly and take a verified backup before upgrading; rollback is full-restore-only. Roll out Cloudly `33.19.2` before Coreflow `33.2.0`; Coreflow `33.1.0` is the minimum. From `1.3.0`, also roll out Cloudly before Coreflow and Corestore `33.0.0`, and set every node's Corestore database endpoint, because a database binding on a node without one is not granted and its service is refused `database-endpoint-missing`, and existing database bindings return no credentials until they are granted again for the endpoint. From `1.1.10` or earlier, startup first runs the `1.2.0` steps from `0.10.0` and needs completed Secrets v2 cutover retirement. Both the existing Secrets v2 keyring and operator-approved WorkloadInit authority are required encrypted inputs delivered as separate root-owned `0400` files; the public environment contains only their fixed paths. |
|
|
71
|
+
| `cloudly` | MongoDB, S3 | Version `1.30.0` also declares Cloudly's runtime log ledger non-backup data: `databaseBackupExclusions` names the collections `cloudly_runtime_log_attempts`, `cloudly_runtime_log_batches`, `cloudly_runtime_log_captures`, `cloudly_runtime_log_inline_payloads`, `cloudly_runtime_log_node_discards` and `cloudly_runtime_log_queue` of its `mongodb` database, and `requiresFeatures` names `storage.database.backup-exclusions` beside `storage.object-storage.backup-exclusions`, so a host that does not fulfil the database feature lists it, refuses it as a target and keeps `1.29.4` as its newest. A backup on a host that fulfils it exports those collections with their indexes and without their documents, so a restore brings them back empty; every Cloudly `33.21.0` start re-records the log attempts that still run. Version `1.30.0` is `1.29.4` with the Cloudly `33.21.0` image and the ledger declaration and is the upgrade target of `1.29.4`, `1.29.3`, `1.29.2`, `1.29.1`, `1.29.0`, `1.28.6`, `1.28.5` and `1.28.3`; Cloudly `33.21.0` adds no data step, no collection and no index and keeps the data version `0.31.0`. Versions `1.30.0`, `1.29.4`, `1.29.3`, `1.29.2`, `1.29.1`, `1.29.0`, `1.28.6` and `1.28.5` declare Cloudly's workload logs non-backup data: `backupExclusions` names the key prefix `runtime-logs/` of the `legacy-s3` bucket, and `requiresFeatures` names `storage.object-storage.backup-exclusions`, so a host that does not fulfil it lists them and refuses them as targets. A backup on a host that fulfils it leaves those objects out, and a restore brings back Cloudly's state, not its past workload logs. Version `1.29.4` is `1.29.3` with the Cloudly `33.20.4` image and is the upgrade target of `1.29.3`, `1.29.2`, `1.29.1`, `1.29.0`, `1.28.6`, `1.28.5` and `1.28.3`. Cloudly `33.20.4` keeps the runtime log sweep bounded by its own work: its reads no longer scan the whole runtime log ledger, each sweep step commits at most 16 rows under a 15 s deadline instead of retrying past the database's transaction lifetime, and each step first lets critical node requests (such as the managed VPN credential renewal) through, so a large ledger no longer stalls node credentials. It adds no data step, no collection and no index and keeps the data version `0.31.0`, so an upgrade from `1.29.3` or `1.29.2` runs none. Version `1.29.3` is `1.29.2` with the Cloudly `33.20.3` image and is the upgrade target of `1.29.2`, `1.29.1`, `1.29.0`, `1.28.6`, `1.28.5` and `1.28.3`. Cloudly `33.20.3` lets a service that already holds a Corestore binding of a capability adopt its own legacy resource of that capability into that binding: Corestore `34.1.4` and later refuse a binding beside an unauthorized legacy resource (`legacy-adoption-pending`), and `adoptCorestoreLegacyResources` refused such a service `binding-exists`, so the service stayed down; now the adoption is written onto the matching enabled canonical binding in the adoption's transaction, and every other binding still refuses `binding-exists`. It adds no data step and no collection and keeps the data version `0.31.0`, so an upgrade from `1.29.2` runs none; an upgrade from `1.29.1` or earlier also takes the changes of Cloudly `33.20.2` below. Version `1.29.2` is `1.29.1` with the Cloudly `33.20.2` image and is the upgrade target of `1.29.1`, `1.29.0`, `1.28.6`, `1.28.5` and `1.28.3`. Cloudly `33.20.2` keeps its registry storage bounded: a storage retention runs 3 minutes after start, then daily, prunes releases no plan, running tag, deployment operation or unfinished rollout needs beyond each service's 3 most recently promoted accepted releases and the last 7 days, and deletes manifests and blobs nothing reaches after 24 hours, in time-bounded holds of the registry mutation lock; the per-push prune is gone, and a service deletion deletes only its own repository's manifests and tags. A rollout of what a replica already runs keeps it running, and gateway, mail and CoreMail status reported under a service lifecycle lease is kept, one record per observer in `cloudly_service_reconciliation_stages`. Its startup runs the forward-only data step `service-reconciliation-observations` from `0.30.0` to `0.31.0`; every Cloudly before `33.20.2` refuses `0.31.0`, so a return to `1.29.1` or earlier means restoring the backup taken before the upgrade, and registry objects the retention deleted do not come back with it. An upgrade from `1.29.0` or earlier also takes the changes of Cloudly `33.20.1` below. Version `1.29.1` is `1.29.0` with the Cloudly `33.20.1` image and is the upgrade target of `1.29.0`, `1.28.6`, `1.28.5` and `1.28.3`. Cloudly `33.20.1` no longer blanks a cluster's ingress routes when it restarts: its first table of a cluster is the full one, never an empty revocation, a node that only registers again keeps its routes because a route endpoint is a node instance's statement under its credential generation (`nodeInstanceNonce`), unreadable route sources keep the relay's table for at most 180 s after a start, and a revocation withdraws exactly the revoked routes. A Pallet registration Cloudly cannot serve for a reason of its own is refused as momentary (`controller-unavailable`, `controller-deadline`; @serve.zone/interfaces `32.57.0`), so the node registers again instead of ending its session, and a credential check whose store cannot be read fails closed. It adds no data step and no collection and keeps the data version `0.30.0`, so an upgrade from `1.29.0` runs none; an upgrade from `1.28.6` or earlier also takes the changes of Cloudly `33.20.0` below. Version `1.29.0` is `1.28.6` with the Cloudly `33.20.0` image and is the upgrade target of `1.28.6` and `1.28.5`. Cloudly `33.20.0` serves a non-public hostname from a node's cluster ingress to dcrouter alone once the setting `dcrouterForwardSourceAddresses` names dcrouter's forwarding sources (the cluster's Coreflow and CoreTraffic must run releases on `@serve.zone/interfaces` `32.55.0` or later), writes workload log payloads as one segment object per capture every few minutes, keeping a committed batch inline in the new collection `cloudly_runtime_log_inline_payloads` until its segment is written, and lets a service whose retained rollout plan cannot run be redeployed (`RECOVERY_REDEPLOY_OVER_FAILED_RUNTIME`). It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.28.6` runs none; its first start creates that collection, which after an unclean stop of the previous Cloudly fails with `EFENCE_BUSY` for up to 30 minutes on Corestore `32.2.4`. Cloudly `33.19.2` and earlier refuse the log ledger rows `33.20.0` writes, so a return to an earlier template needs the backup taken before the upgrade. `1.28.6` is `1.28.4` (Cloudly `33.19.2`) with that declaration; `1.28.5` is `1.28.3` (Cloudly `33.19.1`) with it, so a host on `1.28.3` moves its template without moving Cloudly. `1.28.5` sorts above `1.28.4` but runs the older image: from `1.28.4`, upgrade to `1.28.6`. Version `1.28.4` pins Cloudly `33.19.2` and keeps the `1.28.3` runtime contract, including the Onebox `33.8.0` minimum: its platform Corestore `32.2.4` runs NoSQLDB `10.6.1` with the D3 write-conflict fix, and on an earlier engine a node's network plan loses every commit to a `WriteConflict`, so Cloudly `33.15` must not run on it. Cloudly `33.19.2` is a fixes-only patch over `33.19.1`: a greenfield deployment, a legacy import's successor included, proves its namespace blank on the nodes of the Pallet clusters only, so a fleet that still keeps a Coreflow-owned Swarm cluster without a relay can reserve one, while a Pallet node whose inventory cannot be read still blocks; a greenfield rollout without a Pallet runtime spec is refused `GREENFIELD_PALLET_RUNTIME_SPEC_REQUIRED` instead of falling back to Coreflow placement (state the spec with `setServiceRuntimeSpec`, then promote again); and a node's network compilation proves each workload lease and each authority or eligibility proof once per transaction. It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.28.3` runs none. The `1.28.4` changelog carries the changes of `33.19.2` and of `33.18.3` to `33.19.1` and the earlier releases' upgrade path only, because a catalog changelog is bounded at 65536 characters; `1.28.2`'s carries the rest. From `1.28.2` or earlier, Cloudly `33.19.1`'s changes apply too: Cloudly `33.19.1` carries `33.18.3` to `33.19.1`: a node's log report costs one append transaction that queues the batch with its payload in the new collection `cloudly_runtime_log_queue`, and a background drain writes the payloads and commits the ledger rows without holding the node's request or fencing the controller record (a full queue refuses a report with the retryable `log-intake-saturated`), object writes list nothing, workload logs are kept for at least 30 days, route verification reads the certificate each route presents on a connection of its own, and `readServiceDeploymentStatus` (interfaces `32.54.0`) reads a deployment operation without changing it. Its first start creates `cloudly_runtime_log_queue`; on NoSQLDB `10.6.1` that creation waits at most 5 s for the open transactions on the Cloudly database, and a transaction an earlier Cloudly left open by exiting before its stop completed holds the database until its session expires 30 minutes later, so after an unclean stop of the previous Cloudly start the upgrade no earlier than 30 minutes after that exit. It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.28.2`, `1.28.1` or `1.28.0` runs none. From `1.28.1` or earlier, Cloudly `33.18.2`'s fixes apply too: Cloudly `33.18.2` is a fixes-only patch over `33.18.1`: a node's network configuration advances only once the node's application receipt names the projection it holds, so a node no longer receives a new version while it still applies the last one; upgrade every node to Pallet `36.3.0` or later (interfaces `32.50.0` or later) first, because a node that sends no receipt keeps the projection it holds and receives no successor. It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.28.1` or `1.28.0` runs none. From `1.28.0` or earlier, Cloudly `33.18.1`'s fixes apply too: Cloudly `33.18.1` is a fixes-only patch over `33.18.0`: a node's log batches append without its node fences, so they no longer miss their budget against the node's producer passes, and the deployment preflight answers within 25 s, past which it answers the retryable `DEPLOYMENT_PREFLIGHT_UNAVAILABLE`. It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.28.0` runs none. From `1.27.0` or earlier, Cloudly `33.18.0`'s changes apply too: Cloudly `33.18.0` loads the App Store catalog at runtime instead of bundling it, from the registries of its `appStoreCatalogRegistries` setting (empty means the npm registry), so a Cloudly template update needs an appstore release only; it ignores `APPSTORE_URL`, whose remote source is removed, and while no verified catalog is active it refuses App Store install details, upgrade preview and upgrade with `APPSTORE_CATALOG_UNAVAILABLE`. It adds no data step and keeps the data version `0.30.0`, so an upgrade from `1.27.0` runs none. From `1.26.0` or earlier, Cloudly `33.17.3`'s changes apply too: Cloudly `33.17.3` moves the data version from `0.28.0` to `0.30.0`: every upgrade from `1.26.0` or earlier runs the forward-only data steps `route-claim-absent-owners` to `0.29.0` (the stored nulls of absent owners are removed from every route claim) and `bounded-runtime-history` to `0.30.0` (every archived deployment record is deleted, the index `archiveState_1` is dropped and the accepted Spark observations are pruned to the rows the census reads), and every Cloudly before `33.17.3` refuses the `0.30.0` data. Runtime history is bounded from then on: a Coreflow report deletes the deployment records it no longer names instead of archiving them, `getDeployments` answers the current records whether or not it is asked to `includeArchived`, `deploymentArchiveRetentionDays` is accepted but has no effect, and the dashboard no longer offers archived deployments. A deployment preflight no longer fails `DEPLOYMENT_PREFLIGHT_UNAVAILABLE` on a route claim that stores an absent owner as `null`, and the deployment and adoption preflights log the cause of an unexpected failure. A node's requests no longer contend on the controller fence and share a 6 s budget below the cluster relay's 8 s forward deadline, a request out of budget is refused `controller-deadline`, which the node retries, and a node deletion refuses what a racing write bound to the node. Cloudly consumes the projection application receipts of Pallet `36.3.0`, so a flow a node's projection withdrew is granted again on the same leases once the node reports that it applied the withdrawal; an earlier Pallet keeps every withdrawal carried. Cloudly `33.17.2` starts a database below data version `0.25.0` that holds a runtime slot: the data step `runtime-log-attempts` read through models startup has not prepared and was refused, and now reads and writes through the driver; an install whose first start failed there stayed at data version `0.24.0` and upgrades to `1.28.4` normally. Cloudly `33.17.1` fixes the first start of `33.17.0`, which crash-looped on a database holding Pallet-era nodes: its first data step `cluster-node-hostname-intent` stated each intent through the node and cluster models, which startup has not prepared while it runs the data steps, and was refused before its first write; the step now reads and writes the stored node and cluster documents directly, like the other data steps. The migration ledger does not advance past a failed step, so an install on `1.25.0` whose first start failed stayed unchanged at data version `0.25.0` and upgrades to `1.28.4` normally. Cloudly `33.17.1` keeps the data steps of `33.17.0`, which move the data version from `0.25.0` to `0.28.0`: every upgrade from `1.24.0` or earlier, and from `1.25.0` whose first start failed, runs the forward-only data steps `cluster-node-hostname-intent` to `0.26.0` (every Pallet-era node gets the intent of the hostname it reports, and nothing is renamed), `deployment-operation-route-evidence` to `0.27.0` (a routeless deployment operation that succeeded records its empty route evidence) and `service-secret-target-predecessor` to `0.28.0` (a secret target state an earlier service deletion left behind is removed) before the `0.28.0` to `0.30.0` steps. The template stays marked breaking, migration-required, backup-before-upgrade and manual-review, because hosts read the flags from the target version alone and every upgrade from `1.26.0` or earlier runs the `0.28.0` to `0.30.0` steps, after the `0.25.0` to `0.28.0` steps from `1.24.0` or earlier; every upgrade from `1.19.0` or earlier runs the forward-only data step `runtime-log-attempts` from `0.24.0` to `0.25.0` before them (the log record of every slot's current attempt is written), and every upgrade from `1.15.0` or earlier runs `corestore-control-token-state` from `0.23.0` to `0.24.0` before that (a Corestore control token stored earlier is recorded as revision 1); every Cloudly before `33.17.3` refuses the migrated data: stop Cloudly and take a verified backup first, as rollback is full-restore-only. From `1.24.0` or earlier, Cloudly `33.17.0`'s changes apply too: Cloudly `33.17.0` lets a legacy import reserve: it takes over every route claim its legacy service holds in the import's organization, the cluster ingress keeps serving each hostname for the legacy service until the import's service declares it, and cleanup or rollback before promotion hands the claims back while the legacy service exists; `ROUTE_UNAVAILABLE` and `DEPLOYMENT_ROUTE_COLLISION` name each conflicting hostname and the services holding it. It owns the hostname a node should carry (interfaces `32.52.0`, `cloudly-worker-<name>` by default): `setClusterNodeHostname` renames a Pallet-era node and `resetClusterNodeHostname` sets the default, Spark reads the intent at `POST /spark/nodes/host`, and a Pallet service's gateway routes and inbound mail follow the node its runtime spec pins by id, so a rename moves no traffic. A routeless deployment records its empty route evidence when it succeeds; deleting a cluster's designated ingress service is refused `SERVICE_IS_CLUSTER_INGRESS`, `designateClusterIngress` recovers a designation whose bearer is no longer active, and a deleted service's secret target state goes with its row, so a service recreated under its id writes its secrets again. From `1.23.0` or earlier, Cloudly `33.16.0`'s changes apply too: Cloudly `33.16.0` captures a legacy service's runtime attestation from an administrator's host observation, without a Coreflow session: `captureLegacyServiceRuntimeAttestation` takes `hostObservation`, the legacy Swarm service's spec image and mode and, per replica, its node, Swarm task, container, image store, image id, `RepoDigests`, state and health, read at most 15 minutes before the capture; Cloudly judges it against the one root every replica runs, which it requires whole in its registry and which the legacy tag need not still name, and refuses incomplete evidence without storing anything. The registry prune keeps an attested root until a release's `cloudly-legacy-import` evidence names the attestation or the legacy service is retired. From `1.22.0` or earlier, Cloudly `33.15.3`'s changes apply too: Cloudly `33.15.3` serves a promoted route: a route's identity carried the service's runtime write fence `runtimeAuthorityFenceGeneration`, which every runtime write of the service advances, in a Pallet-born cluster the route join's own workload lease read included, so the route plan read after the cluster ingress's certificate fetch never matched the one before it and every refresh that fetched a certificate withdrew the cluster's whole route table; a route's identity now carries only the versions that change with its authority (the route intent, the runtime policy revision, and per endpoint its assignment, its node's session and its target), while a service being deleted or holding no runtime authority is still refused by the route join, so a revoked route is withdrawn as before, by name. A table withdrawn because its authority changed or its route sources could not be read names that withdrawal as the reason the cluster ingress serves none of its routes, so a failing route verification states it; that reason states facts about its own route only, because one table carries the routes of several organizations: a route whose authority changed names its changed field, every other route only that another route's authority (or the ingress designation) changed during the certificate fetch, and a source-read failure only that the route sources could not be read, its error staying in the operator log. From `1.21.0` or earlier, Cloudly `33.15.2`'s changes apply too: Cloudly `33.15.2` lets a promoted route reach CoreTraffic: a route's identity carries the route intent version `routeIntentGeneration` instead of the whole-document `routeStateGeneration`, so the status writes during the cluster ingress's certificate fetch no longer withdraw the cluster's route table, a plan change during the fetch withdraws it only when it revokes a route the table is about to serve, and the change is named by route and input. A cluster ingress flow that a compensated promotion withdrew and a later promotion wants again no longer stalls the node's network projection: Cloudly holds that flow back with its withdrawal carried and signs the rest of the node's network, while a workload or router grant wanted again while withdrawn withholds the node's projection as `withdrawn-grant-reissued` and any other contract refusal as `projection-rejected`, each naming what it holds; the cluster ingress leaves a route whose grant is held out by name, and a failed route verification states why no route is served for its hostname. A held flow is granted again only once the target or CoreTraffic runs under a new lease. From `1.20.0` or earlier, Cloudly `33.15.1`'s changes apply too: Cloudly `33.15.1` fixes the deployment preflight of an existing service Pallet runs: it read the current runtime from Coreflow alone and answered NO-GO (`CURRENT_RUNTIME_DIGEST_MISSING`, `RUNTIME_ATTESTATION_MISMATCH`) for a service whose rollout status was verified, and now reads the runs of the service's current plan, the evidence its deployment status reads, when Pallet executes that plan on every cluster it targets. It also lets a route promotion survive the status writes of its own gateway sync: the promotion fenced on `routeStateGeneration`, the service's whole-document version, which the route status its gateway sync records, Coreflow's reconciliation reports and its own verified-route status all advance, so every promotion failed `DEPLOYMENT_ROUTE_FENCE_CONFLICT` with no route evidence; it now fences on `routeIntentGeneration`, which only a write that changes the service's domains advances, so a concurrent route change, a change and back included, still fails it fenced, and a promotion interrupted under `33.15.0` is recovered as fenced (retryable), without a rollback. From `1.19.0` or earlier, Cloudly `33.15.0`'s changes apply too: Cloudly `33.15.0` keeps workload logs: a Pallet node's log batches are kept per attempt, their payloads in Cloudly's own bucket (the template's `S3_BUCKET`) under `runtime-logs/`, bounded to 8 MiB and 8192 batches per attempt and the newest 8 attempts per slot, each batch for its config's `logs.retentionMs`, and read with `getServiceRuntimeLogAttempts` and `getRuntimeAssignmentLogs` (interfaces `32.47.0`) or the service page's Logs tab. A Pallet-executed rollout that succeeded stays succeeded when a replica later fails or is replaced; a failing service runtime pass names its step, and a database error its code name and number. A launcher secret written before a service's first image plan no longer leaves the service refused `secrets-unsupported`, because every image plan is written with the service's secret deployment plan in one transaction. The designated cluster ingress runs with `SERVEZONE_CLUSTER_RELAY_ORIGIN` and `SERVEZONE_CLUSTER_NODE_ID`, which Cloudly writes into that workload's environment (a cluster without a valid relay refuses it `cluster-relay-missing`); the template sets neither. From `1.18.0` or earlier, Cloudly `33.14.1` and `33.14.2`'s changes apply too: Cloudly `33.14.2` completes a rollout Pallet executes: Cloudly reports on every rollout whose target clusters are all on Pallet, one reporter per cluster (`pallet/<clusterId>`) over the runs of exactly the current desire, so it moves to `succeeded` (or `ready-for-route`) once its runs are ready at the requested digest and fails when a run fails, and `getServiceDeploymentStatus` reads its runtime tasks from the runtime manager instead of Coreflow, so tsdeploy no longer times out on a Pallet-attached service. A launcher-secret run admitted after a Cloudly restart, reconnect or credential rotation gets its material again instead of waiting `secret-mount-uncertain` (`secret-recipient-inactive`) until its node process restarts, because a node's secret recipient is active for every session of the node process that enrolled it. A failing producer pass is logged under its producer's subject once per change of cause, with how long it ran and a value-free cause naming the failing step and check (such as `network-projection-rejected`), and its recovery once; a failed sweep the timer started is no longer dropped. From `1.17.0` or earlier, Cloudly `33.14.0`'s changes apply too: it gives a platform-owned service's published ports infrastructure-administrator authority: an administrator designates a platform organization's service (an organization id carrying `:`) with `designatePlatformOwnedService`, withdraws it with `undesignatePlatformOwnedService` and reads it with `getServicePlatformOwnership` (interfaces `32.45.0`), after which only a verified administrator reads and writes its published ports, any other organization id is refused `organization-not-platform`, and the designation, stored on the service document, is read inside the published-port request's one admitted operation; every organization service keeps the organization-owner rule. It also names and logs every assignment dispatch outcome under a closed, value-free reason, pushes a refused or unanswered push again on the next pass, and keeps a run its node refused (cause `refused`) instead of replacing it on the failure backoff until its input changes. From `1.16.0` or earlier, Cloudly `33.13.3` and `33.13.4`'s changes apply too: Cloudly `33.13.4` delivers a run's secret material through the WorkloadInit launcher again (Corestore's `MONGODB_URI` included), remembers a successful WorkloadInit approval verification per artifact, and names the check of every refused material request with a closed, value-free `reason`; since Cloudly `33.13.3` a node's session is pushed each assignment revision once instead of on every delivery pass. From `1.15.0` or earlier, Cloudly `33.11.0` to `33.13.2`'s changes apply too: they let an administrator set the Corestore control token without an exec into Cloudly's container (`setCorestoreControlToken`, interfaces `32.42.0`, with its revision and value-free receipts in the new collections `cloudly_corestore_control_token_state` and `cloudly_corestore_control_token_receipts`, and `servezone corestore set-control-token`), keep every managed VPN member across a protected-authority step that only adds, retire a node and its egress owner (`retireClusterNode`, interfaces `32.43.0`), ask a retiring node on interfaces `32.44.0` to withdraw its allocation protection so that a stopped or offline node no longer blocks allocation, and keep a stopping workload's lease in its node's projection until the node's stop receipt is accepted. From `1.14.0` or earlier, Cloudly `33.10.0`'s changes apply too: it lets an operator obtain a cluster relay's bearer without an exec into Cloudly's process: `exportClusterRelayAuthorization` (interfaces `32.41.0`) requires a freshly signed-in administrator, proves the delivered credential version against the hash its generation issued, commits an audit receipt fenced at that generation before it answers the bearer, and never logs it; the receipts live in the new collection `cloudly_cluster_relay_authorization_exports`, installed at startup and listed by `getClusterRelayAuthorizationExports`, and `servezone relay export-authorization` (`@serve.zone/cli` `33.10.0`) writes the bearer as a new `0600` file. From `1.14.0` or earlier, Cloudly `33.9.0`'s changes apply too: a node's managed VPN member is kept across its network projection changes instead of being withdrawn from the hub network on every workload change (`bindGetRuntimeManagedVpnCredentialSuccessor`, interfaces `32.39.0`), the failure cause a node reports with a failed run (interfaces `32.40.0`) is stored and shown in the runtime assignment views and the dashboard's Cause column, and `deleteServiceById` answers success once a service is marked for deletion while another deleting service drains, naming its refusals `SERVICE_LIFECYCLE_BUSY`, `SERVICE_ORGANIZATION_AUTHORITY_REQUIRED` and `SERVICE_MANAGER_STOPPING` instead of an internal error. From `1.13.0` or earlier, Cloudly `33.8.3`'s fix applies too: it lets `deleteServiceById` retire a legacy service whose Corestore resources a successor adopted: its live Corestore bindings are tombstoned with the ordinary config push once every claimed legacy resource of each binding's capability has passed to the successor's live adopting binding, while a service without such a handover is still refused `CORESTORE_CLEANUP_TARGET_AUTHORITY_REQUIRED`. A legacy service whose runtime attestation a legacy-import operation still uses (`reserving`, `reserved`, `provisioning` or `awaiting-image`) is refused `SERVICE_LEGACY_IMPORT_PENDING` (retryable) and keeps running; import and deletion are serialized on the registry repository, so an import that starts after the check refuses as `LEGACY_IMPORT_SOURCE_MISMATCH`, and a deletion that proceeds deletes the attestations no release consumed. From `1.12.0` or earlier, Cloudly `33.8.2`'s fix applies too: Cloudly names why it refused a node's report: a refused `reportRuntimeNetworkProtection`, `reportRuntimeAssignmentTerminalReceipt`, `reportRuntimeAssignmentObservation` or `reportRuntimeAssignmentLogs` is logged as a warning once per node, report and distinct reason, and the node is answered with the unchanged error text plus `{ code: 'runtime-session-report-refused', reason }` as the error's data. From `1.11.0` or earlier, Cloudly `33.8.1`'s fix applies too: deploy before any Pallet that sends `discardedCaptures` or a `not-kept` log loss reaches a fleet node, since it accepts those log batch fields of interfaces `32.38.0`, which Cloudly `33.8.0` refuses on every retry, and still keeps no workload logs. From `1.10.0` or earlier, Cloudly `33.8.0`'s changes apply too: deploy before any Pallet that captures workload output reaches a fleet node, since Cloudly `33.8.0` answers a Pallet's `reportRuntimeAssignmentLogs` (interfaces `32.37.0`) with `not-kept`, which an earlier Cloudly leaves unhandled. Cloudly `33.8.0` lets a legacy `deployOnPush` service without deployable source move to a greenfield successor that runs the exact image it ran (`captureLegacyServiceRuntimeAttestation`, a `legacyImport` reservation under the capability `deployment:import-legacy`, and `importServiceDeploymentLegacyImage` in place of a push), judges promotion against the platforms of the service's rollout target, refuses a registry repository that belongs to another service as `REGISTRY_REPOSITORY_MISMATCH`, promotes and routes a service with a Corestore `database` or `objectstorage` binding again, and reads a node whose last report is older than 180 seconds as `offline`. Its web UI moved to path URLs and a regrouped menu, so bookmarks of the old UI locations open the not-found screen; server paths such as `/status` and the OIDC callback are unchanged. From `1.9.0` or earlier, Cloudly `33.7.0`'s changes apply: Cloudly `33.7.0` lets a fresh greenfield service take over a legacy service's Corestore database and bucket with their data and retention intact: an `adoptCorestoreLegacyResources` selection entry may name `successorServiceId` (interfaces `32.34.0`, carried out by Corestore `33.2.0` or later), the successor must share the owner's organization, be an immutable-deployment service, own no legacy resource of the capability and hold no other binding of it, and each legacy resource is adopted by one service only (`legacy-resource-adopted-elsewhere`, `successor-organization-mismatch`, `successor-not-greenfield`, `successor-owns-legacy-resource`). Since Cloudly `33.6.1`, a deployment reservation whose release tag the registry already holds is refused `DEPLOYMENT_RELEASE_TAG_OCCUPIED` instead of waiting for its image forever, an administrator's manifest push to a release tag a deployment operation reserved is refused `409 release-tag-reserved`, and a binding status report reaches its cluster relay only when it changed what the relay reads. From `1.8.0` or earlier, startup runs the forward-only data step `deployment-machine-user-grants` from `0.22.0` to `0.23.0`, which keeps on every deployment machine user only the grants a capability check reads and permanently removes the rest (such as the `{ serviceId, capabilities }` grants of Cloudly 11); stop Cloudly and take a verified backup first, as the way back is the pre-upgrade backup. Cloudly `33.6.0`'s changes apply too: Cloudly stops gracefully on SIGTERM and SIGINT within 8 seconds, below the 10-second stop grace of Docker and Swarm, so a stop or restart no longer leaves a database transaction held and the next start no longer crash-loops on `EFENCE_BUSY`; a process ended by SIGKILL still holds its database session until the server's session timeout. Every new service gets its own deployment namespace, so two services of one project reserve without `DEPLOYMENT_RESOURCE_COLLISION`, while a deployed service keeps its namespace, and `getDeploymentMachineUsers` lists users configured under Cloudly 11 again. From `1.7.0` or earlier, Cloudly `33.5.0`'s changes apply: Cloudly reserves a deployment only for a deployment machine user: `reserveServiceDeployment` refuses an administrator with `DEPLOYMENT_MACHINE_IDENTITY_REQUIRED`, including a deployer that signs in with a username and password (such as tsdeploy with `TSDEPLOY_CLOUDLY_USERNAME`/`TSDEPLOY_CLOUDLY_PASSWORD`), so before upgrading create a deployment machine user per deployer (`mutateDeploymentMachineUser`, or Security → Access → Deployment machine users in the dashboard), attach its grants with `configureServiceDeploymentMachineUser` and switch the deployer to its token; an existing `api` machine user with its grants already qualifies. A machine identity or registry bearer issued by an earlier release is refused until its holder exchanges its token again with `getIdentityByToken`, and removing a token now ends the sessions opened with it. Let no deployment run during the upgrade: a run in progress keeps its pre-upgrade identity and is refused for the rest of that run (rerun it, or resume it with `deploy-retry`). From `1.3.0` through `1.6.0`, startup runs the forward-only data step `runtime-session-protocol-offer` from `0.21.0` to `0.22.0`, which states `protocol: null` on every Pallet session row recorded before Cloudly stored its node's protocol offer; stop Cloudly and take a verified backup first, as rollback is full-restore-only. From `1.6.0`, Cloudly `33.4.0`'s changes apply: Cloudly `33.4.0` lets `setServicePublishedPorts` publish a contiguous range through `hostPortEnd` (at most 1024 ports in 64 entries per protocol) and same-port outbound through `symmetric: true`, which needs public egress (`published-port-symmetric-without-egress`) and owns its inside ports (`published-port-symmetric-inside-overlap`). New handoff leases translate outbound flows only into `49152-65535`, whose ports are refused `published-port-reserved`; a node keeps its `1024-65535` lease until its router is re-created. A policy write for a service being deleted is refused `service-deleting`. A projection carrying a range or a symmetric entry is withheld as `node-contract-behind` from a Pallet node whose session states a release below interfaces `32.31.0`, or an unrecorded one, so ranges and symmetric entries take effect on a node only once it runs a Pallet release built on interfaces `32.31.0` or later. From `1.5.0`, Cloudly `33.3.0`'s changes apply: Cloudly `33.3.0` lets an administrator set a service's network egress with `setServiceNetworkEgress` (public internet, and the platform endpoints of the protected authority), and refuses a workload whose database or object-storage binding sits on a node whose Corestore endpoint its network does not select as `database-endpoint-unselected` or `objectstorage-endpoint-unselected`; a replica already running keeps running while its target reports the refusal. From `1.4.0`, Cloudly `33.2.0`'s changes apply: Cloudly takes a dcrouter admin token once through `bootstrapExternalGatewayCredential` to provision its own gateway-client credential, starts a first enrollment with the gateway settings, and stages a gateway settings change the held credential cannot carry until the next bootstrap. From `1.2.1` or earlier, startup runs the forward-only migration from `0.20.0` to `0.21.0` before that step, dropping the relay certificate and DNS collections of Cloudly's retired Cloudflare and Let's Encrypt integration and logging every relay name whose A record Cloudly had written at Cloudflare, which the operator removes at the provider. dcrouter holds every cluster relay name's A record and certificate from then on, `SERVEZONE_SSLMODE=letsencrypt` is refused, and the typed API changes as the Cloudly 33.0.0 changelog lists. Stop Cloudly and take a verified backup before upgrading; rollback is full-restore-only. Roll out Cloudly `33.19.2` before Coreflow `33.2.0`; Coreflow `33.1.0` is the minimum. From `1.3.0`, also roll out Cloudly before Coreflow and Corestore `33.0.0`, and set every node's Corestore database endpoint, because a database binding on a node without one is not granted and its service is refused `database-endpoint-missing`, and existing database bindings return no credentials until they are granted again for the endpoint. From `1.1.10` or earlier, startup first runs the `1.2.0` steps from `0.10.0` and needs completed Secrets v2 cutover retirement. Both the existing Secrets v2 keyring and operator-approved WorkloadInit authority are required encrypted inputs delivered as separate root-owned `0400` files; the public environment contains only their fixed paths. |
|
|
72
72
|
| `ghost` | MariaDB | Uses MySQL-style `database__connection__*` environment variables. |
|
|
73
73
|
| `gitea` | MariaDB | Uses Gitea database environment variables with `${MARIADB_*}` placeholders. |
|
|
74
74
|
| `nextcloud` | MariaDB, Valkey | Uses MySQL-compatible database variables. Valkey is declared as a platform requirement. |
|
|
@@ -277,6 +277,29 @@ of backing up what it was told to leave out or ignoring the declaration:
|
|
|
277
277
|
}
|
|
278
278
|
```
|
|
279
279
|
|
|
280
|
+
`databaseBackupExclusions` declares database collections an app keeps only for
|
|
281
|
+
observability, such as a workload log ledger: at most one entry per database
|
|
282
|
+
(`mongodb`, which `platformRequirements.mongodb` provisions), 1 to 64 collection
|
|
283
|
+
names in strictly increasing order and one `reason`. A backup exports those
|
|
284
|
+
collections with their indexes and without their documents, so a restore brings
|
|
285
|
+
them back empty; the app must rebuild what it needs of them on start. A version
|
|
286
|
+
that declares one must name `storage.database.backup-exclusions` in
|
|
287
|
+
`requiresFeatures`:
|
|
288
|
+
|
|
289
|
+
```jsonc
|
|
290
|
+
{
|
|
291
|
+
"platformRequirements": { "mongodb": true },
|
|
292
|
+
"databaseBackupExclusions": [
|
|
293
|
+
{
|
|
294
|
+
"database": "mongodb",
|
|
295
|
+
"collections": ["app_log_batches", "app_log_queue"],
|
|
296
|
+
"reason": "Workload log ledger; a restore brings back state, not past logs."
|
|
297
|
+
}
|
|
298
|
+
],
|
|
299
|
+
"requiresFeatures": ["storage.database.backup-exclusions"]
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
280
303
|
The full version config and every storage object are exact-key validated.
|
|
281
304
|
Classes express portable policy only. Provider addresses, protocols,
|
|
282
305
|
identities, host paths, drivers, and driver options are rejected instead of
|
|
@@ -298,9 +321,11 @@ const normalizedStorage = normalizeAppStoreStorageConfig(catalogVersion.config,
|
|
|
298
321
|
```
|
|
299
322
|
|
|
300
323
|
The result contains canonical `storageClasses`, `storageRequests`,
|
|
301
|
-
`requestOrigins`, `requiredFeatureIds
|
|
324
|
+
`requestOrigins`, `requiredFeatureIds`, `backupExclusions`, the declared
|
|
302
325
|
non-backup key prefixes and reason by object-storage request id (`legacy-s3` for
|
|
303
|
-
`platformRequirements.s3`, empty when the version declares none)
|
|
326
|
+
`platformRequirements.s3`, empty when the version declares none), and
|
|
327
|
+
`databaseBackupExclusions`, the collections and reason a backup exports without
|
|
328
|
+
documents by database (`mongodb`, empty when the version declares none). `requestOrigins` marks explicit,
|
|
304
329
|
legacy-volume, and legacy-S3 requests so fulfillment adapters can preserve old
|
|
305
330
|
delivery behavior without weakening the new contract. Legacy S3 normalization
|
|
306
331
|
uses the `servezone-platform-s3` delivery profile; adapters keep
|
package/ts_client/readme.md
CHANGED
|
@@ -30,7 +30,7 @@ if (result.outcome === 'activated') {
|
|
|
30
30
|
- `AppStoreCatalogLoader` loads the catalog package from the registry list `appstore.resolveAppStoreCatalogRegistries` (`@serve.zone/interfaces`) answers, npmjs by default, checks the tarball's `dist.integrity`, verifies the envelope against the consumer's trusted keys and supported feature ids, and judges its succession; `signal` aborts a load. `restoreEnvelopeText` verifies a stored last-known-good envelope again at start. `judgeImportedEnvelope` and `judgeImportedEnvelopeText` judge an operator-imported envelope the same way as a fetched one. It holds no state, runs no timer and persists nothing; every request is bounded.
|
|
31
31
|
- `findAppStoreCatalogApp` and `findAppStoreCatalogVersion` look up the active catalog; withdrawn versions stay listed (`appstore.judgeAppStoreCatalogInstallTarget` refuses them as targets).
|
|
32
32
|
- `projectAppStoreCatalogApp`, `projectAppStoreCatalog` and `getAppStoreCatalogListing` project the active catalog for one consumer (its supported feature ids, kind and release): every listed version with its install refusal (`versionWithdrawn`, `featuresUnsupported` or `consumerVersionTooOld`), the newest install target, and the `IAppStoreApp` / `IAppStoreAppMeta` shapes the App Store views carry, listing install targets in `versions` and every version in `versionStates`.
|
|
33
|
-
- `normalizeAppStoreStorageConfig` and `normalizeAppStoreVolumes` produce the storage handoff of a catalog version config after the interfaces contract judged its storage; the handoff's `backupExclusions` maps each object-storage request id (`legacy-s3` for `platformRequirements.s3`) to the key prefixes a backup leaves out and why.
|
|
33
|
+
- `normalizeAppStoreStorageConfig` and `normalizeAppStoreVolumes` produce the storage handoff of a catalog version config after the interfaces contract judged its storage; the handoff's `backupExclusions` maps each object-storage request id (`legacy-s3` for `platformRequirements.s3`) to the key prefixes a backup leaves out and why, and its `databaseBackupExclusions` maps each provisioned database (`mongodb`) to the collections a backup exports without their documents and why; a declaration derives `storage.database.backup-exclusions` into `requiredFeatureIds`, so a host that does not advertise it refuses the version.
|
|
34
34
|
- `appStoreCatalogPackageName` and `appStoreCatalogEnvelopePackagePath` name where the catalog is published.
|
|
35
35
|
|
|
36
36
|
Test fixtures for consumers live in the separate entry `@serve.zone/appstore/testing`.
|
package/ts_client/storage.ts
CHANGED
|
@@ -9,6 +9,7 @@ type TCatalogVersionConfig = plugins.servezoneInterfaces.appstore.TAppStoreCatal
|
|
|
9
9
|
type TStorageFeatureId = plugins.servezoneInterfaces.appstore.TAppStoreStorageFeatureId;
|
|
10
10
|
type TStorageRequest = plugins.servezoneInterfaces.appstore.TAppStoreStorageRequest;
|
|
11
11
|
type TBackupExclusion = plugins.servezoneInterfaces.appstore.IAppStoreBackupExclusion;
|
|
12
|
+
type TDatabaseBackupExclusion = plugins.servezoneInterfaces.appstore.IAppStoreDatabaseBackupExclusion;
|
|
12
13
|
type TVolume = plugins.servezoneInterfaces.appstore.IAppStoreVolume;
|
|
13
14
|
|
|
14
15
|
const storageFeatureIds = plugins.servezoneInterfaces.appstore.appStoreStorageFeatureIds;
|
|
@@ -60,9 +61,11 @@ const createLegacyVolumeRequestId = (volumeArg: TVolume, indexArg: number, label
|
|
|
60
61
|
const deriveRequiredFeatureIds = (
|
|
61
62
|
explicitRequestsArg: TStorageRequest[],
|
|
62
63
|
backupExclusionsArg: readonly TBackupExclusion[],
|
|
64
|
+
databaseBackupExclusionsArg: readonly TDatabaseBackupExclusion[],
|
|
63
65
|
): TStorageFeatureId[] => {
|
|
64
66
|
const required: TStorageFeatureId[] = [];
|
|
65
67
|
if (explicitRequestsArg.length > 0) required.push(storageFeatureIds.bindings);
|
|
68
|
+
if (databaseBackupExclusionsArg.length > 0) required.push(storageFeatureIds.databaseBackupExclusions);
|
|
66
69
|
if (explicitRequestsArg.some((requestArg) => requestArg.kind === 'filesystem')) {
|
|
67
70
|
required.push(storageFeatureIds.filesystem);
|
|
68
71
|
}
|
|
@@ -96,7 +99,8 @@ export function normalizeAppStoreVolumes(
|
|
|
96
99
|
* The config's storage is judged by the interfaces contract first. Legacy `volumes` become
|
|
97
100
|
* filesystem requests of the reserved `legacyFilesystem` class, `platformRequirements.s3` becomes
|
|
98
101
|
* the `legacy-s3` object-storage request, `backupExclusions` become the declared non-backup key
|
|
99
|
-
* prefixes by request id,
|
|
102
|
+
* prefixes by request id, `databaseBackupExclusions` the collections a backup exports without
|
|
103
|
+
* documents by database, and explicit requests and backup exclusions fail closed unless every
|
|
100
104
|
* storage feature they need is advertised by the target runtime.
|
|
101
105
|
*/
|
|
102
106
|
export function normalizeAppStoreStorageConfig(
|
|
@@ -112,7 +116,8 @@ export function normalizeAppStoreStorageConfig(
|
|
|
112
116
|
const requestOrigins: Record<string, TAppStoreStorageRequestOrigin> = {};
|
|
113
117
|
for (const request of explicitRequests) requestOrigins[request.id] = 'explicit';
|
|
114
118
|
const declaredExclusions = configArg.backupExclusions ?? [];
|
|
115
|
-
const
|
|
119
|
+
const declaredDatabaseExclusions = configArg.databaseBackupExclusions ?? [];
|
|
120
|
+
const requiredFeatureIds = deriveRequiredFeatureIds(explicitRequests, declaredExclusions, declaredDatabaseExclusions);
|
|
116
121
|
|
|
117
122
|
const legacyVolumes = normalizeVolumes(configArg.volumes);
|
|
118
123
|
if (legacyVolumes.length > 0) {
|
|
@@ -182,6 +187,15 @@ export function normalizeAppStoreStorageConfig(
|
|
|
182
187
|
};
|
|
183
188
|
}
|
|
184
189
|
|
|
190
|
+
// The contract accepted every entry: it names a database the version provisions, once.
|
|
191
|
+
const databaseBackupExclusions: INormalizedAppStoreStorageConfig['databaseBackupExclusions'] = {};
|
|
192
|
+
for (const exclusion of declaredDatabaseExclusions) {
|
|
193
|
+
databaseBackupExclusions[exclusion.database] = {
|
|
194
|
+
collections: [...exclusion.collections],
|
|
195
|
+
reason: exclusion.reason,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
185
199
|
const supportedFeatures = new Set(optionsArg.supportedFeatures);
|
|
186
200
|
const unsupportedFeatures = requiredFeatureIds.filter((featureIdArg) => !supportedFeatures.has(featureIdArg));
|
|
187
201
|
if (unsupportedFeatures.length > 0) {
|
|
@@ -194,6 +208,7 @@ export function normalizeAppStoreStorageConfig(
|
|
|
194
208
|
requestOrigins,
|
|
195
209
|
requiredFeatureIds,
|
|
196
210
|
backupExclusions,
|
|
211
|
+
databaseBackupExclusions,
|
|
197
212
|
...(legacyCompatibility ? { legacyCompatibility } : {}),
|
|
198
213
|
};
|
|
199
214
|
}
|
package/ts_client/types.ts
CHANGED
|
@@ -30,6 +30,15 @@ export type TAppStoreNormalizedBackupExclusion = Omit<
|
|
|
30
30
|
'storageRequest'
|
|
31
31
|
>;
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* The collections of one database a backup exports without their documents: the collections of a
|
|
35
|
+
* version's `databaseBackupExclusions` entry for that database and the operator-readable reason.
|
|
36
|
+
*/
|
|
37
|
+
export type TAppStoreNormalizedDatabaseBackupExclusion = Omit<
|
|
38
|
+
plugins.servezoneInterfaces.appstore.IAppStoreDatabaseBackupExclusion,
|
|
39
|
+
'database'
|
|
40
|
+
>;
|
|
41
|
+
|
|
33
42
|
/** The canonical storage handoff a fulfillment adapter receives for one version config. */
|
|
34
43
|
export interface INormalizedAppStoreStorageConfig {
|
|
35
44
|
storageClasses: Record<string, plugins.servezoneInterfaces.appstore.IAppStoreStorageClass>;
|
|
@@ -42,6 +51,15 @@ export interface INormalizedAppStoreStorageConfig {
|
|
|
42
51
|
* objects under these key prefixes out; a restore neither brings them back nor removes them.
|
|
43
52
|
*/
|
|
44
53
|
backupExclusions: Record<string, TAppStoreNormalizedBackupExclusion>;
|
|
54
|
+
/**
|
|
55
|
+
* Declared observability-only collections by database (`mongodb` for
|
|
56
|
+
* `platformRequirements.mongodb`), empty when the version declares none. A backup exports these
|
|
57
|
+
* collections with their indexes and without their documents; a restore brings them back empty.
|
|
58
|
+
*/
|
|
59
|
+
databaseBackupExclusions: Partial<Record<
|
|
60
|
+
plugins.servezoneInterfaces.appstore.TAppStoreBackupExclusionDatabase,
|
|
61
|
+
TAppStoreNormalizedDatabaseBackupExclusion
|
|
62
|
+
>>;
|
|
45
63
|
legacyCompatibility?: IAppStoreStorageLegacyCompatibility;
|
|
46
64
|
}
|
|
47
65
|
|