@selvajs/cli 4.20.0-beta.25 → 4.20.0-beta.26
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/package.json +1 -1
- package/src/caddyfile.js +1 -1
- package/src/checks/config.js +11 -13
- package/src/env-migration.js +12 -5
- package/src/env.js +3 -6
- package/src/generated/config.js +26 -115
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selvajs/cli",
|
|
3
|
-
"version": "4.20.0-beta.
|
|
3
|
+
"version": "4.20.0-beta.26",
|
|
4
4
|
"description": "Scaffold and operate a Selva white-label deployment. `npx @selvajs/cli <dir>` to bootstrap, `selva <cmd>` to manage.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "VektorNode",
|
package/src/caddyfile.js
CHANGED
|
@@ -45,7 +45,7 @@ export function renderCaddyfile({ domain, acmeEmail, port = 3000 }) {
|
|
|
45
45
|
@api path /api/*
|
|
46
46
|
header @api Cache-Control "no-cache, no-store, must-revalidate"
|
|
47
47
|
|
|
48
|
-
# Must clear the app's own caps (
|
|
48
|
+
# Must clear the app's own caps (the solve request cap and BODY_SIZE_LIMIT,
|
|
49
49
|
# both 256 MiB) — Caddy rejects first, so a lower value here silently caps
|
|
50
50
|
# every upload no matter what .env says. Write MiB: Caddy's "mb" is 10^6 bytes.
|
|
51
51
|
request_body {
|
package/src/checks/config.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// it. `__tests__/doctor-config-parity.test.js` asserts both against one fixture
|
|
11
11
|
// table, so a rule changed here and not there fails CI.
|
|
12
12
|
|
|
13
|
-
import { RENAMED_ENV_VARS,
|
|
13
|
+
import { RENAMED_ENV_VARS, RETIRED_ENV_VARS } from '../env.js';
|
|
14
14
|
import { keyRotationStatus } from '../generated/config.js';
|
|
15
15
|
import { green, red, yellow } from './result.js';
|
|
16
16
|
|
|
@@ -221,10 +221,10 @@ export function parseBodySizeLimit(raw) {
|
|
|
221
221
|
return bytes;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
// The app's
|
|
224
|
+
// The app's solve request cap (COMPUTE_REQUEST_MAX_BYTES in packages/server
|
|
225
225
|
// compute/limits.ts). Duplicated rather than imported: the CLI installs the
|
|
226
226
|
// runtime and cannot import from it.
|
|
227
|
-
const
|
|
227
|
+
const COMPUTE_REQUEST_MAX_BYTES = 256 * 1024 * 1024;
|
|
228
228
|
|
|
229
229
|
/**
|
|
230
230
|
* BODY_SIZE_LIMIT has to clear the compute request cap, or adapter-node's global
|
|
@@ -234,7 +234,7 @@ const COMPUTE_REQUEST_DEFAULT_BYTES = 256 * 1024 * 1024;
|
|
|
234
234
|
*/
|
|
235
235
|
export function checkBodySizeLimit(env) {
|
|
236
236
|
const raw = env.BODY_SIZE_LIMIT;
|
|
237
|
-
const requestCap =
|
|
237
|
+
const requestCap = COMPUTE_REQUEST_MAX_BYTES;
|
|
238
238
|
const asMb = (bytes) => `${Math.round(bytes / (1024 * 1024))} MB`;
|
|
239
239
|
|
|
240
240
|
if (raw == null || raw === '') {
|
|
@@ -255,9 +255,9 @@ export function checkBodySizeLimit(env) {
|
|
|
255
255
|
|
|
256
256
|
if (bytes < requestCap) {
|
|
257
257
|
return yellow(
|
|
258
|
-
`BODY_SIZE_LIMIT=${raw} (${asMb(bytes)}) is below
|
|
259
|
-
|
|
260
|
-
`
|
|
258
|
+
`BODY_SIZE_LIMIT=${raw} (${asMb(bytes)}) is below the ` +
|
|
259
|
+
`${asMb(requestCap)} a solve may send — larger solves fail with 413 before Selva ` +
|
|
260
|
+
`sees them. Raise it to at least ${asMb(requestCap)}.`
|
|
261
261
|
);
|
|
262
262
|
}
|
|
263
263
|
|
|
@@ -331,9 +331,7 @@ export function checkClientAddress(env) {
|
|
|
331
331
|
return green(`ADDRESS_HEADER=${header}${depth ? ` (XFF_DEPTH=${depth})` : ''}`);
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
//
|
|
335
|
-
// encodes a value in the new name, so migrate leaves it alone rather than
|
|
336
|
-
// guessing — it's reported here and nowhere else.
|
|
334
|
+
// Both are auto-fixable: `selva migrate` renames old names and removes retired keys.
|
|
337
335
|
export function checkDeprecatedEnv(env) {
|
|
338
336
|
const checks = [];
|
|
339
337
|
|
|
@@ -346,9 +344,9 @@ export function checkDeprecatedEnv(env) {
|
|
|
346
344
|
);
|
|
347
345
|
}
|
|
348
346
|
|
|
349
|
-
for (const [
|
|
350
|
-
if (env[
|
|
351
|
-
checks.push(yellow(`${
|
|
347
|
+
for (const [key, now] of Object.entries(RETIRED_ENV_VARS)) {
|
|
348
|
+
if (env[key] === undefined) continue;
|
|
349
|
+
checks.push(yellow(`${key} is no longer read (${now}) — \`selva migrate\` removes it`));
|
|
352
350
|
}
|
|
353
351
|
|
|
354
352
|
return checks;
|
package/src/env-migration.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Plan the .env rewrite `selva migrate` applies: old names renamed, values a
|
|
2
|
-
// looser reader used to accept rewritten to what they meant, no-op
|
|
3
|
-
// removed, shipped template prose stripped. Unknown keys are kept.
|
|
2
|
+
// looser reader used to accept rewritten to what they meant, no-op and retired
|
|
3
|
+
// lines removed, shipped template prose stripped. Unknown keys are kept.
|
|
4
4
|
//
|
|
5
5
|
// `mismatches` is the proof: every declared key resolved from the old file
|
|
6
6
|
// (with the old readers' semantics) and from the new one. Anything but empty
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
countEnvCommentLines,
|
|
11
11
|
parseEnv,
|
|
12
12
|
RENAMED_ENV_VARS as RENAMES,
|
|
13
|
+
RETIRED_ENV_VARS as RETIRED,
|
|
13
14
|
renameEnvKeys,
|
|
14
15
|
setEnvValues,
|
|
15
16
|
stripEnvComments
|
|
@@ -34,8 +35,7 @@ function computeDebugMode(env) {
|
|
|
34
35
|
/** Keys whose effective value depends on another key. */
|
|
35
36
|
const EFFECTIVE = {
|
|
36
37
|
HEADER_AUTH_DATA_DIR: (env) => env.HEADER_AUTH_DATA_DIR || env.DATA_PATH,
|
|
37
|
-
SELVA_FLAG_COMPUTE_DEBUG: computeDebugMode
|
|
38
|
-
SELVA_FLAG_COMPUTE_DEBUG_VERBOSE: () => undefined
|
|
38
|
+
SELVA_FLAG_COMPUTE_DEBUG: computeDebugMode
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
function canonical(value) {
|
|
@@ -114,6 +114,13 @@ export function planEnvMigration(input) {
|
|
|
114
114
|
changes.push({ key: 'HEADER_AUTH_DATA_DIR', action: 'removed', reason: 'same as DATA_PATH' });
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
// The app ignores these whatever their value, so removing the line changes nothing it does.
|
|
118
|
+
for (const [key, now] of Object.entries(RETIRED)) {
|
|
119
|
+
if (env[key] === undefined || key in updates) continue;
|
|
120
|
+
updates[key] = undefined;
|
|
121
|
+
changes.push({ key, action: 'removed', reason: `no longer read: ${now}` });
|
|
122
|
+
}
|
|
123
|
+
|
|
117
124
|
if (Object.keys(updates).length > 0) text = setEnvValues(text, updates);
|
|
118
125
|
|
|
119
126
|
// Read back with Node's parser, the one the app boots with, so a writer bug
|
|
@@ -127,7 +134,7 @@ export function planEnvMigration(input) {
|
|
|
127
134
|
if (JSON.stringify(was) !== JSON.stringify(is)) mismatches.push({ key: spec.key, was, is });
|
|
128
135
|
}
|
|
129
136
|
for (const key of Object.keys(before)) {
|
|
130
|
-
if (!SPECS.has(key) && !(key in RENAMES) && after[key] !== before[key]) {
|
|
137
|
+
if (!SPECS.has(key) && !(key in RENAMES) && !(key in RETIRED) && after[key] !== before[key]) {
|
|
131
138
|
mismatches.push({ key, was: before[key], is: after[key] });
|
|
132
139
|
}
|
|
133
140
|
}
|
package/src/env.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { readFileSync, writeFileSync, existsSync, chmodSync } from 'node:fs';
|
|
6
6
|
import { parseEnv as nodeParseEnv } from 'node:util';
|
|
7
|
-
import { ALL_SETTINGS } from './generated/config.js';
|
|
7
|
+
import { ALL_SETTINGS, RETIRED_KEYS } from './generated/config.js';
|
|
8
8
|
|
|
9
9
|
// Old env name → its replacement, for keys renamed without changing meaning.
|
|
10
10
|
// From the manifest's `aliases`, the same list the server reads old names by.
|
|
@@ -12,11 +12,8 @@ export const RENAMED_ENV_VARS = Object.fromEntries(
|
|
|
12
12
|
ALL_SETTINGS.flatMap((s) => (s.aliases ?? []).map((alias) => [alias, s.key]))
|
|
13
13
|
);
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
export const REPLACED_ENV_VARS = {
|
|
18
|
-
SELVA_FLAG_COMPUTE_DEBUG_VERBOSE: 'SELVA_FLAG_COMPUTE_DEBUG=verbose'
|
|
19
|
-
};
|
|
15
|
+
// Keys the app no longer reads → what took their place. `selva migrate` removes them.
|
|
16
|
+
export const RETIRED_ENV_VARS = RETIRED_KEYS;
|
|
20
17
|
|
|
21
18
|
// Header written above the settings in a stripped .env. The pointer matters:
|
|
22
19
|
// without it the operator has no way back to what a var means, and the reflex
|
package/src/generated/config.js
CHANGED
|
@@ -682,68 +682,15 @@ var ENGINE_SETTINGS = composeManifest([
|
|
|
682
682
|
description: 'Override the TLS mode inferred from the port.',
|
|
683
683
|
legacy: { bool: 'exact-true' }
|
|
684
684
|
}),
|
|
685
|
-
tunableInt('COMPUTE_SOLVE_DEADLINE_MS', 1e5, 'Solve time limit', 'Longest one solve may run.', {
|
|
686
|
-
unit: 'ms',
|
|
687
|
-
aliases: ['MAX_SOLVE_DURATION_MS'],
|
|
688
|
-
impact: 'Past the reverse proxy read timeout, solves fail there with a 502 instead.'
|
|
689
|
-
}),
|
|
690
|
-
tunableInt(
|
|
691
|
-
'COMPUTE_RATE_LIMIT_WINDOW_MS',
|
|
692
|
-
1e5,
|
|
693
|
-
'Solve rate limit window',
|
|
694
|
-
'Compute rate-limit window.',
|
|
695
|
-
{ unit: 'ms' }
|
|
696
|
-
),
|
|
697
685
|
tunableInt(
|
|
698
686
|
'COMPUTE_RATE_LIMIT_MAX',
|
|
699
687
|
120,
|
|
700
688
|
'Solves per window',
|
|
701
|
-
'Solves per user or share link per window.'
|
|
689
|
+
'Solves per user or share link per 100-second window.'
|
|
702
690
|
),
|
|
703
691
|
tunableInt('MAX_DEFINITION_FILE_SIZE_BYTES', 50 * MB, 'Max .gh upload', 'Largest .gh upload.', {
|
|
704
692
|
unit: 'bytes'
|
|
705
693
|
}),
|
|
706
|
-
tunableInt(
|
|
707
|
-
'MAX_IMAGE_FILE_SIZE_BYTES',
|
|
708
|
-
10 * MB,
|
|
709
|
-
'Max image upload',
|
|
710
|
-
'Largest cover image upload.',
|
|
711
|
-
{ unit: 'bytes' }
|
|
712
|
-
),
|
|
713
|
-
tunableInt(
|
|
714
|
-
'COMPUTE_REQUEST_MAX_BYTES',
|
|
715
|
-
256 * MB,
|
|
716
|
-
'Max solve request',
|
|
717
|
-
'Largest solve request body.',
|
|
718
|
-
{
|
|
719
|
-
unit: 'bytes',
|
|
720
|
-
impact: 'Must stay at or below BODY_SIZE_LIMIT, or that limit rejects first.'
|
|
721
|
-
}
|
|
722
|
-
),
|
|
723
|
-
tunableInt(
|
|
724
|
-
'COMPUTE_RESPONSE_MAX_BYTES',
|
|
725
|
-
300 * MB,
|
|
726
|
-
'Max solve response',
|
|
727
|
-
'Largest solve response body.',
|
|
728
|
-
{ unit: 'bytes' }
|
|
729
|
-
),
|
|
730
|
-
tunableInt(
|
|
731
|
-
'REMOTE_DEFINITION_FETCH_TIMEOUT_MS',
|
|
732
|
-
3e4,
|
|
733
|
-
'Remote .gh fetch timeout',
|
|
734
|
-
'Deadline for fetching a remote .gh.',
|
|
735
|
-
{ unit: 'ms' }
|
|
736
|
-
),
|
|
737
|
-
tunableInt(
|
|
738
|
-
'REMOTE_DEFINITION_CACHE_TTL_MS',
|
|
739
|
-
3e5,
|
|
740
|
-
'Remote .gh cache time',
|
|
741
|
-
'How long remote .gh bytes stay cached.',
|
|
742
|
-
{
|
|
743
|
-
unit: 'ms',
|
|
744
|
-
aliases: ['DEFINITION_CACHE_TTL_MS']
|
|
745
|
-
}
|
|
746
|
-
),
|
|
747
694
|
tunableInt(
|
|
748
695
|
'COMPUTE_MAX_QUEUE_DEPTH',
|
|
749
696
|
0,
|
|
@@ -789,17 +736,6 @@ var ENGINE_SETTINGS = composeManifest([
|
|
|
789
736
|
aliases: ['COMPUTE_RESPONSE_CACHE_MB']
|
|
790
737
|
}
|
|
791
738
|
),
|
|
792
|
-
defineSetting({
|
|
793
|
-
key: 'COMPUTE_REUSE_DEFINITION_CACHE',
|
|
794
|
-
label: 'Reuse compute server definition cache',
|
|
795
|
-
type: 'bool',
|
|
796
|
-
default: true,
|
|
797
|
-
onInvalid: 'default',
|
|
798
|
-
class: 'tunable',
|
|
799
|
-
description: 'Reuse the compute server’s cached definition instead of re-uploading it.',
|
|
800
|
-
impact:
|
|
801
|
-
'Leave on only with a compute server that reports a stale pointer (the VektorNode fork).'
|
|
802
|
-
}),
|
|
803
739
|
defineSetting({
|
|
804
740
|
key: 'COMPUTE_SERVER_CACHESOLVE',
|
|
805
741
|
label: 'Compute server solve cache',
|
|
@@ -809,18 +745,11 @@ var ENGINE_SETTINGS = composeManifest([
|
|
|
809
745
|
class: 'tunable',
|
|
810
746
|
description: 'Ask Rhino.Compute to cache solve results server-side.'
|
|
811
747
|
}),
|
|
812
|
-
tunableInt(
|
|
813
|
-
'API_TOKEN_RATE_LIMIT_WINDOW_MS',
|
|
814
|
-
DEFAULT_API_RATE_LIMIT.windowMs,
|
|
815
|
-
'API rate limit window',
|
|
816
|
-
'API token rate-limit window.',
|
|
817
|
-
{ unit: 'ms' }
|
|
818
|
-
),
|
|
819
748
|
tunableInt(
|
|
820
749
|
'API_TOKEN_RATE_LIMIT_MAX',
|
|
821
750
|
DEFAULT_API_RATE_LIMIT.maxPerWindow,
|
|
822
|
-
'API requests per
|
|
823
|
-
'Requests per API token per
|
|
751
|
+
'API requests per minute',
|
|
752
|
+
'Requests per API token per minute. 0 is off.',
|
|
824
753
|
{
|
|
825
754
|
min: 0,
|
|
826
755
|
zero: 'Off'
|
|
@@ -946,29 +875,6 @@ var SUPABASE_SETTINGS = composeManifest([
|
|
|
946
875
|
class: 'setting',
|
|
947
876
|
description: 'OAuth buttons on the login page (google, github, azure, gitlab).'
|
|
948
877
|
}),
|
|
949
|
-
defineSetting({
|
|
950
|
-
key: 'SUPABASE_TOKEN_VERIFICATION',
|
|
951
|
-
label: 'Session check mode',
|
|
952
|
-
type: 'enum',
|
|
953
|
-
values: ['hybrid', 'strict'],
|
|
954
|
-
default: 'hybrid',
|
|
955
|
-
onInvalid: 'fail',
|
|
956
|
-
class: 'tunable',
|
|
957
|
-
description:
|
|
958
|
-
'hybrid verifies JWTs locally and rechecks periodically; strict asks Supabase every request.',
|
|
959
|
-
legacy: { enumFallback: 'hybrid' }
|
|
960
|
-
}),
|
|
961
|
-
defineSetting({
|
|
962
|
-
key: 'SUPABASE_REVALIDATE_MS',
|
|
963
|
-
label: 'Session recheck interval',
|
|
964
|
-
type: 'int',
|
|
965
|
-
min: 1,
|
|
966
|
-
default: 6e4,
|
|
967
|
-
unit: 'ms',
|
|
968
|
-
onInvalid: 'default',
|
|
969
|
-
class: 'tunable',
|
|
970
|
-
description: 'Hybrid recheck window: the longest a revoked session keeps working.'
|
|
971
|
-
}),
|
|
972
878
|
defineSetting({
|
|
973
879
|
key: 'SUPABASE_PUBLIC_BUCKET',
|
|
974
880
|
label: 'Public files bucket',
|
|
@@ -986,15 +892,6 @@ var SUPABASE_SETTINGS = composeManifest([
|
|
|
986
892
|
onInvalid: 'fail',
|
|
987
893
|
class: 'tunable',
|
|
988
894
|
description: 'Storage bucket for private files.'
|
|
989
|
-
}),
|
|
990
|
-
defineSetting({
|
|
991
|
-
key: 'SUPABASE_PRIVATE_URL_PREFIX',
|
|
992
|
-
label: 'Private file URL prefix',
|
|
993
|
-
type: 'string',
|
|
994
|
-
default: '/api/files',
|
|
995
|
-
onInvalid: 'fail',
|
|
996
|
-
class: 'tunable',
|
|
997
|
-
description: 'URL prefix the app serves private files at.'
|
|
998
895
|
})
|
|
999
896
|
]);
|
|
1000
897
|
// src/lib/server/config/settings.ts
|
|
@@ -1147,14 +1044,6 @@ var APP_SETTINGS = composeManifest([
|
|
|
1147
1044
|
'off, on (cache and timing lines per solve) or verbose (adds Compute’s raw error bodies).',
|
|
1148
1045
|
impact: 'verbose can write what was solved to the log, which erasure cannot reach.'
|
|
1149
1046
|
}),
|
|
1150
|
-
defineSetting({
|
|
1151
|
-
key: 'SELVA_FLAG_COMPUTE_DEBUG_VERBOSE',
|
|
1152
|
-
label: 'Compute debug logging (old name)',
|
|
1153
|
-
type: 'bool',
|
|
1154
|
-
onInvalid: 'default',
|
|
1155
|
-
class: 'setting',
|
|
1156
|
-
description: 'Replaced by SELVA_FLAG_COMPUTE_DEBUG=verbose.'
|
|
1157
|
-
}),
|
|
1158
1047
|
defineSetting({
|
|
1159
1048
|
key: 'SENTRY_DSN',
|
|
1160
1049
|
label: 'Sentry error reporting',
|
|
@@ -1199,6 +1088,27 @@ var WATCHED_PREFIXES = [
|
|
|
1199
1088
|
];
|
|
1200
1089
|
/** Read by tooling, not by the app: not unknown, not reported. */
|
|
1201
1090
|
var EXTERNAL_KEYS = ['SELVA_REQUIRE_SUPABASE'];
|
|
1091
|
+
/**
|
|
1092
|
+
* Keys no release reads any more, with what took their place. Boot and doctor
|
|
1093
|
+
* name them instead of calling them typos; `selva migrate` removes them.
|
|
1094
|
+
*/
|
|
1095
|
+
var RETIRED_KEYS = {
|
|
1096
|
+
SELVA_FLAG_COMPUTE_DEBUG_VERBOSE: 'use SELVA_FLAG_COMPUTE_DEBUG=verbose',
|
|
1097
|
+
COMPUTE_RATE_LIMIT_WINDOW_MS: 'fixed at 100 s',
|
|
1098
|
+
API_TOKEN_RATE_LIMIT_WINDOW_MS: 'fixed at 60 s',
|
|
1099
|
+
MAX_IMAGE_FILE_SIZE_BYTES: 'fixed at 10 MB',
|
|
1100
|
+
COMPUTE_REQUEST_MAX_BYTES: 'fixed at 256 MB',
|
|
1101
|
+
COMPUTE_RESPONSE_MAX_BYTES: 'fixed at 300 MB',
|
|
1102
|
+
REMOTE_DEFINITION_FETCH_TIMEOUT_MS: 'fixed at 30 s',
|
|
1103
|
+
REMOTE_DEFINITION_CACHE_TTL_MS: 'fixed at 5 min',
|
|
1104
|
+
DEFINITION_CACHE_TTL_MS: 'fixed at 5 min',
|
|
1105
|
+
SUPABASE_TOKEN_VERIFICATION: 'always hybrid',
|
|
1106
|
+
SUPABASE_REVALIDATE_MS: 'fixed at 60 s',
|
|
1107
|
+
SUPABASE_PRIVATE_URL_PREFIX: 'fixed at /api/files',
|
|
1108
|
+
COMPUTE_SOLVE_DEADLINE_MS: 'set --timeout (RHINO_COMPUTE_TIMEOUT) on the compute server',
|
|
1109
|
+
MAX_SOLVE_DURATION_MS: 'set --timeout (RHINO_COMPUTE_TIMEOUT) on the compute server',
|
|
1110
|
+
COMPUTE_REUSE_DEFINITION_CACHE: 'on whenever the compute server reports /limits'
|
|
1111
|
+
};
|
|
1202
1112
|
/**
|
|
1203
1113
|
* The manifest for this deployment: engine and app keys, plus the slices of
|
|
1204
1114
|
* the providers it selects. A provider's required keys only apply when it is
|
|
@@ -1235,7 +1145,7 @@ function deploymentReport(env) {
|
|
|
1235
1145
|
);
|
|
1236
1146
|
return buildConfigReport(manifest, env, {
|
|
1237
1147
|
prefixes: WATCHED_PREFIXES,
|
|
1238
|
-
external: [...EXTERNAL_KEYS, ...inactive]
|
|
1148
|
+
external: [...EXTERNAL_KEYS, ...Object.keys(RETIRED_KEYS), ...inactive]
|
|
1239
1149
|
});
|
|
1240
1150
|
}
|
|
1241
1151
|
var NOT_FROM_ENV_FILE = /* @__PURE__ */ new Set(['NODE_ENV']);
|
|
@@ -1262,6 +1172,7 @@ function keyRotationStatus(env, now = Date.now()) {
|
|
|
1262
1172
|
export {
|
|
1263
1173
|
ALL_SETTINGS,
|
|
1264
1174
|
PREVIOUS_KEY_DAYS,
|
|
1175
|
+
RETIRED_KEYS,
|
|
1265
1176
|
buildConfigReport,
|
|
1266
1177
|
deploymentManifest,
|
|
1267
1178
|
deploymentReport,
|