-
Notifications
You must be signed in to change notification settings - Fork 67
Superseded: wrong follow-up branch #898
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2596381
1133ff5
b0f72ce
d614702
12fe077
bccde22
0abdb4e
aa60720
eaf3e29
92fecc3
fdc6278
915cf1e
b9b5666
a540076
198c21e
7abc5fb
6155496
62d2481
02ef093
ff5f4c5
c78ac21
bf3df6a
d13226c
f94187c
1bb6939
058f744
c061980
cc26594
eb5af86
227987f
ef0c1c1
4f90fca
cad6f89
883eaea
38d861a
85c740d
60a3945
bc93678
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -76,6 +76,47 @@ before migrations and deploy. Preview deploys do the same per preview worker via | |
| resources from bindings alone, so the deploy workflow runs | ||
| `node tools/ci/production-resources.ts ensure` first. | ||
|
|
||
| ### Disaster-recovery control plane | ||
|
|
||
| Production backups use a separate deployment and are not provisioned or deployed | ||
| by the application workflow above. The dedicated Worker and Workflow live under | ||
| `packages/backup-control-plane/` in an independently administered DR Cloudflare | ||
| account. Its `BACKUP_BUCKET` R2 binding is private and uses immutable `daily/` | ||
| and `weekly/` prefixes. | ||
|
|
||
| The retention administrator uses a separate provisioner token to create the | ||
| bucket and apply 35-day daily and 400-day weekly lock/lifecycle rules: | ||
|
|
||
| ```sh | ||
| node tools/ci/backup-resources-cli.ts plan \ | ||
| --source-account-id "<PRODUCTION_ACCOUNT_ID>" \ | ||
| --destination-account-id "<DR_ACCOUNT_ID>" \ | ||
| --source-d1 "<PRODUCTION_D1_UUID>:kody" \ | ||
| --deny-production-resource kody-email-blobs \ | ||
| --deny-production-resource kody-community-assets | ||
| ``` | ||
|
Comment on lines
+90
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Finding relevant files..."
git ls-files | rg '(^docs/contributing/setup-manifest\.md$|^docs/.*/disaster-recovery\.md$|backup-resources-cli\.ts$|disaster-recovery\.md$)' || true
echo
echo "Relevant snippet in setup-manifest.md:"
sed -n '70,125p' docs/contributing/setup-manifest.md || true
echo
echo "Search for backup-resources-cli usages and docs:"
rg -n "backup-resources-cli|backup-resources|source-d1|destination-account-id|bucket-name|worker-name|provisioner|provide.*token" docs disaster-recovery setup-manifest tools packages 2>/dev/null || trueRepository: kentcdodds/kody Length of output: 50372 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "setup-manifest relevant section:"
sed -n '1,150p' docs/contributing/setup-manifest.md | nl -ba | sed -n '70,130p'
echo
echo "Exact relevant flag usages in tracked text files (narrow):"
rg -n --glob '!*.{min.js,js.map,tsbuildinfo,min.css}' --glob '*.md' --glob '*.ts' --glob '*.tsx' \
"backup-resources-cli|backup-resources|plan.*--bucket-name|plan.*--worker-name|supplying|provisioner|PROVISION|token" \
docs disaster-recovery README packages tools 2>/dev/null || true
echo
echo "Locate backup-resources-cli implementation:"
fd -a 'backup-resources-cli\.ts$' . 2>/dev/null || trueRepository: kentcdodds/kody Length of output: 226 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "setup-manifest relevant section:"
sed -n '70,130p' docs/contributing/setup-manifest.md
echo
echo "backup CLI related occurrences in repository text files:"
rg -n --glob '!*.{min.js,js.map,tsbuildinfo,min.css}' --glob '*.md' --glob '*.ts' --glob '*.tsx' \
"backup-resources-cli|backup-resources|\\-\\-bucket-name|\\-\\-worker-name|provisioner|PROVISION|plan" \
docs packages tools disaster-recovery 2>/dev/null || true
echo
echo "Locate backup-resources-cli implementation:"
fd -a 'backup-resources-cli\.ts$' . 2>/dev/null || find . -name 'backup-resources-cli.ts' -printRepository: kentcdodds/kody Length of output: 50374 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "tools/ci/backup-resources-cli.ts argument parsing:"
sed -n '1,170p' tools/ci/backup-resources-cli.ts
echo
echo "tools/ci/backup-resources.ts resource construction relevant sections:"
sed -n '1,160p' tools/ci/backup-resources.ts
sed -n '480,550p' tools/ci/backup-resources.ts
echo
echo "disaster-recovery canonical example around plan/apply:"
sed -n '240,290p' docs/contributing/disaster-recovery.md
echo
echo "backup control plane wrangler constants for bucket/worker if present:"
rg -n "DR_R2_PROVISIONER_TOKEN|BACKUP_BUCKET|backup-resources-cli|kody-production-d1-backups|bucket-name|worker-name" packages/backup-control-plane docs/contributing/disaster-recovery.md tools/ci -g '!*.{min.js,js.map,tsbuildinfo}' || trueRepository: kentcdodds/kody Length of output: 16672 Copy the canonical backup resources example. The setup-manifest snippet omits the provisioner token source plus the explicit 🤖 Prompt for AI Agents |
||
|
|
||
| `apply` is an explicit mutation and must be run only after reviewing the plan. | ||
| The runtime receives a source-account token with Cloudflare Account D1 Edit as | ||
| the `CLOUDFLARE_API_TOKEN` Worker secret. Cloudflare grants this permission | ||
| account-wide and it can mutate D1; the runtime's application UUID/name allowlist | ||
| reduces operator mistakes but does not technically scope the token to one | ||
| database or make it read-only. Keep this source runtime token separate from the | ||
| destination R2 provisioning/lock-administration token and drill restore | ||
| credentials. The runtime must not receive either of those credentials or R2 | ||
| bucket, lock, lifecycle, or public-access administration permissions. Scheduling | ||
| remains inert until the blocking-export benchmark is approved and both enable | ||
| variables are exactly `true`. See [Disaster recovery](./disaster-recovery.md) | ||
| for deployment, readiness, drill, credential, and exclusion details. | ||
|
|
||
| The backup deployment also requires reviewed non-secret | ||
| `BACKUP_MANIFEST_SIGNING_KEY_ID`, `TRUSTED_RESTORE_BASELINE_ID`, and | ||
| `TRUSTED_RESTORE_BASELINE_SHA256` vars. Store the matching base64-encoded | ||
| Ed25519 PKCS#8 private key only as the | ||
| `BACKUP_MANIFEST_SIGNING_PRIVATE_KEY_PKCS8_BASE64` Worker secret. Never commit | ||
|
Comment on lines
+112
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Fix the sentence grammar. Change “requires reviewed non-secret” to “requires the following reviewed non-secret” (or “requires review of the following non-secret”). 🧰 Tools🪛 LanguageTool[style] ~112-~112: The double modal “requires reviewed” is nonstandard (only accepted in certain dialects). Consider “to be reviewed”. (NEEDS_FIXED) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| that private key. Restore trusts only the checked-in manifest public-key, | ||
| production-identity, and restore-baseline registries. | ||
|
|
||
| ## Optional Cloudflare offerings | ||
|
|
||
| The default footprint stays intentionally small. If you want to add additional | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,6 +32,10 @@ | |
| "build:client:web": "esbuild packages/worker/client/entry.tsx --bundle --format=esm --target=es2022 --outdir=packages/worker/public --entry-names=client-entry --chunk-names=assets/[name] --asset-names=assets/[name] --jsx=automatic --jsx-import-source=remix/ui --minify", | ||
| "build:client": "npm run build:client:web", | ||
| "build": "nx run worker:build", | ||
| "backup:build": "wrangler deploy --dry-run --config packages/backup-control-plane/wrangler.jsonc", | ||
| "backup:readiness": "node tools/disaster-recovery/canonical-readiness-cli.ts", | ||
| "backup:resources": "node tools/ci/backup-resources-cli.ts", | ||
| "backup:restore-drill": "node tools/disaster-recovery/d1-restore-drill-cli.ts", | ||
| "lint": "oxlint .", | ||
| "lint:fix": "oxlint . --fix", | ||
| "format": "oxfmt", | ||
|
|
@@ -44,11 +48,11 @@ | |
| "preview": "nx run worker:build-client && npm run migrate:local && node --env-file=packages/worker/.env ./wrangler-env.ts dev --local", | ||
| "preview:e2e": "node --env-file=packages/worker/.env tools/prepare-e2e-env.ts && nx run worker:build-client && npm run migrate:e2e && node --env-file=packages/worker/.env ./wrangler-env.ts dev --local --persist-to .wrangler/state/e2e", | ||
| "generate-types": "node --env-file=packages/worker/.env ./wrangler-env.ts types ./packages/worker/worker-configuration.d.ts", | ||
| "typecheck": "nx run worker:typecheck", | ||
| "typecheck": "nx run worker:typecheck && tsc --noEmit -p packages/backup-control-plane/tsconfig.json", | ||
| "test": "nx run worker:test", | ||
| "test:push": "npm run test && npm run test:e2e:run", | ||
| "inspect": "npx -y @mcpjam/inspector inspector", | ||
| "validate": "concurrently -n format,lint,typecheck,test,e2e,mcp,primitives,migrations -c green,yellow,magenta,blue,cyan,red,white,gray \"npm run format:check\" \"npm run lint\" \"npm run typecheck\" \"npm run test\" \"npm run test:e2e:run\" \"npm run test:mcp\" \"npm run primitives:check\" \"npm run migrations:check\"", | ||
| "validate": "concurrently -n format,lint,typecheck,test,e2e,mcp,backup-build,primitives,migrations -c green,yellow,magenta,blue,cyan,red,blueBright,white,gray \"npm run format:check\" \"npm run lint\" \"npm run typecheck\" \"npm run test\" \"npm run test:e2e:run\" \"npm run test:mcp\" \"npm run backup:build\" \"npm run primitives:check\" \"npm run migrations:check\"", | ||
|
Comment on lines
+51
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
fd -t f 'nx.json'
fd -t f 'project.json' packages/backup-control-plane packages/worker 2>/dev/null
cat packages/backup-control-plane/package.json 2>/dev/null
cat packages/backup-control-plane/vitest.config.ts 2>/dev/null
fd -t f -e yml -e yaml . .github/workflows 2>/dev/null --exec sh -c 'echo "== {} =="; cat {}'
rg -n 'backup-control-plane|disaster-recovery|node\.test' package.json nx.json 2>/dev/nullRepository: kentcdodds/kody Length of output: 44530 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== files =="
fd -t f 'package.json|project.json|nx.json|vitest.config.ts|vitest.config.*|tsconfig.json|tsconfig.*' \
packages/worker packages/backup-control-plane tools/disaster-recovery 2>/dev/null || true
echo
echo "== package.json scripts =="
node - <<'JS'
const fs = require('node:fs');
const p = JSON.parse(fs.readFileSync('package.json','utf8'));
for (const [k,v] of Object.entries(p.scripts || {})) {
if (/^(test|typecheck|backup|validate|migrations|primitives)|backup|disaster-recovery/.test(k)) {
console.log(`${k}: ${v}`);
}
}
JS
echo
echo "== nx/workspace refs =="
rg -n '"workspace|apps|projects|implicitDeps|targetDefaults|backup-control-plane|worker:test|backup-control-plane:test|test":' package.json nx.json packages packages/backup-control-plane/project.json packages/worker/project.json 2>/dev/null || true
echo
echo "== disaster-recovery config/package files =="
fd -t f -e ts . tools/disaster-recovery 2>/dev/null --exec sh -c 'echo "-- {} --"; wc -l "{}"; sed -n "1,220p" "{}"'Repository: kentcdodds/kody Length of output: 50371 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== scripts of interest =="
sed -n '1,140p' package.json
echo
echo "== project files =="
for f in nx.json packages/worker/project.json packages/backup-control-plane/project.json; do
echo "--- $f ---"
sed -n '1,220p' "$f"
done
echo
echo "== validate workflow direct call =="
sed -n '1,130p' .github/workflows/validate.yml
echo
echo "== exact references to backup-control-plane and worker:test =="
rg -n 'backup-control-plane|worker:test|backup-control-plane:test|validate:|concurrently|npm run validate|test":' package.json package-lock.json .github/workflows packages . -g '!node_modules' -g '!dist' -g '!build' 2>/dev/null | sed -n '1,260p'Repository: kentcdodds/kody Length of output: 10625 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== worker/project vitest config files =="
fd -t f 'vitest.config.*|vite.config.*|package.json|project.json' packages/worker -x sh -c 'echo "--- $1 ---"; sed -n "1,220p" "$1"' sh {}
echo
echo "== backup-control-plane project/package/vitest files =="
fd -t f 'vitest.config.*|vite.config.*|package.json|project.json' packages/backup-control-plane -x sh -c 'echo "--- $1 ---"; sed -n "1,220p" "$1"' sh {}
echo
echo "== disaster-recovery test/vitest files only =="
fd -t f -i 'vitest.config.*|*.test.ts|*.spec.ts' tools/disaster-recovery packages/backup-control-plane 2>/dev/null -x sh -c 'echo "--- $1 ---"; sed -n "1,220p" "$1"' sh {}
echo
echo "== precise validate script decomposition =="
python3 - <<'PY'
import json
with open('package.json') as f:
p=json.load(f)
scripts=[]
names=[]
for cmd in p['scripts']['validate'].split('" "',):
s=cmd.strip('"')
cmd=s.split(' ')[-1].split(' ')[0]
scripts.append(cmd)
names.append(s.split(' ')[-1].replace('npm run ','').replace('\"',''))
print('\n'.join(f'{k}: {v}' for k,v in zip(names, scripts)))
print('validate_names_count=', len(names))
print('validate_script_count=', len(scripts))
PYRepository: kentcdodds/kody Length of output: 5577 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== all top-level vitest config files =="
fd -t f 'vitest.config.*' . -maxdepth 2 -x sh -c 'echo "--- $1 ---"; sed -n "1,260p" "$1"' sh {}
echo
echo "== packages containing vitest project declarations or configs =="
rg -n 'node-unit|workers-unit|mcp-e2e|defineConfig|projects:' packages -g '*vitest*' -g '*test*' -g '!node_modules' | sed -n '1,320p'
echo
echo "== disaster-recovery test files without project config text =="
fd -t f -i '.*\.node\.test\.ts|.*\.node\.spec\.ts|vitest.*config.*' tools/disaster-recovery packages/backup-control-plane 2>/dev/null -x sh -c 'echo "--- $1 ---"; wc -l "$1"; sed -n "1,160p" "$1"' sh {}
echo
echo "== exact validate invocation in CI =="
sed -n '1,130p' .github/workflows/validate.yml 2>/dev/null || trueRepository: kentcdodds/kody Length of output: 354 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== all top-level vitest config files =="
fd 'vitest.config\.' . -maxdepth 2 -x sh -c 'echo "--- $1 ---"; sed -n "1,260p" "$1"' sh {}
echo
echo "== packages containing vitest project declarations or configs =="
rg -n 'node-unit|workers-unit|mcp-e2e|defineConfig|projects:' packages -g '*vitest*' -g '*test*' -g '!node_modules' | sed -n '1,320p'
echo
echo "== disaster-recovery test files without project config text =="
fd '.*\.node\.test\.ts|.*\.node\.spec\.ts|vitest.*config.*' tools/disaster-recovery packages/backup-control-plane 2>/dev/null -x sh -c 'echo "--- $1 ---"; wc -l "$1"; sed -n "1,160p" "$1"' sh {}
echo
echo "== exact validate invocation in CI =="
sed -n '1,130p' .github/workflows/validate.yml 2>/dev/null || trueRepository: kentcdodds/kody Length of output: 354 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== all top-level vitest config files =="
find . -maxdepth 2 -type f -path '*vitest.config.*' -print -exec sh -c 'echo "--- $1 ---"; sed -n "1,260p" "$1"' sh {} \;
echo
echo "== packages containing vitest project declarations or configs =="
rg -n 'node-unit|workers-unit|mcp-e2e|defineConfig|projects:' packages -g '*vitest*' -g '*test*' -g '!node_modules' | sed -n '1,320p'
echo
echo "== disaster-recovery test files with simple names =="
find tools/disaster-recovery -type f \( -name '*.node.test.ts' -o -name '*.node.spec.ts' -path '*vitest*' \) -print -exec sh -c 'echo "--- $1"; wc -l "$1"; sed -n "1,160p" "$1"' sh {} \;
echo
echo "== exact validate invocation in CI =="
sed -n '1,130p' .github/workflows/validate.yml 2>/dev/null || trueRepository: kentcdodds/kody Length of output: 25778 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== backup-control-plane test files =="
find packages/backup-control-plane -type f -name '*.node.test.ts' -print | sort
echo
echo "== worker vitest config files inside packages/worker =="
find packages/worker -type f -path '*vitest*' -print | sort -u
for f in $(find packages/worker -type f -path '*vitest*' | sort -u); do
echo "--- $f ---"
sed -n '1,260p' "$f"
done
echo
echo "== package.json validate decomposition =="
node - <<'JS'
const fs = require('node:fs');
const p = JSON.parse(fs.readFileSync('package.json','utf8'));
const validate = p.scripts.validate;
const commands = validate.match(/"[^"]+"/g).map(s => s.slice(1, -1).split(' ').at(-1));
const names = validate.split('" "')
.filter(Boolean)
.map(part => part.replace(/^"|"$/, '')
.replace(/npm run[ ]+/, '')
.split(' ')[0]);
console.log('commands=', commands.join(' | '));
console.log('names=', names.join(' | '));
JSRepository: kentcdodds/kody Length of output: 1073 Include the backup/DR test script in
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| "validate:fix": "npm run format && npm run lint:fix", | ||
| "test:e2e:ensure": "node tools/ensure-playwright-browser.ts", | ||
| "test:e2e:run": "nx run worker:test-e2e", | ||
|
|
@@ -95,6 +99,7 @@ | |
| }, | ||
| "packageManager": "npm@11.11.1", | ||
| "workspaces": [ | ||
| "packages/backup-control-plane", | ||
| "packages/shared", | ||
| "packages/worker", | ||
| "packages/mock-servers/*" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Document the required public-access check.
A private
BACKUP_BUCKETWorker binding does not prove that the R2 bucket has nor2.devor custom-domain exposure. The runbook explicitly says the provisioner does not manage those APIs, so add the post-provisioning verification here or link to it.🤖 Prompt for AI Agents