{"_id":"@daanvandenbergh/backupkit","_rev":"15-6881c3337dcc4a34c8ace2283081457b","name":"@daanvandenbergh/backupkit","dist-tags":{"latest":"2.5.0"},"versions":{"1.0.0":{"name":"@daanvandenbergh/backupkit","version":"1.0.0","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@1.0.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"675d39cf4fc0eaa9e0446c1d0ae18202bb8ce30e","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-1.0.0.tgz","fileCount":114,"integrity":"sha512-LL9V3nTqZsF78I9BMWCR4uNAiMJxAkAwGbL/VaSZDhKa1FZwAXfMpxJyqSOkCSy24q3joSlCSznlNrPDA69uyw==","signatures":[{"sig":"MEYCIQDOZFaq0YaZqJW3IRac+NJgnVFtLFUgYigXTYfCXcRhdQIhAIfq5o8JQ4klxeE6TuVYw8YJ418WEpl5a53GZ8rShTl7","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":656927},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"d035e1c03988062333832126b0b9c6b96b3d8a37","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && npm publish"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.3.0","@daanvandenbergh/scribekit":"^1.7.2"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_1.0.0_1786466427654_0.46335441668461996","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@daanvandenbergh/backupkit","version":"1.0.1","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@1.0.1","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"d26bdfcc9f617631de8833e97a3a954629880096","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-1.0.1.tgz","fileCount":114,"integrity":"sha512-WB9Q+wSVYrCAQNAkalwD+5puTLU9XLH+1YAJhFEvT/NFYXOwP5YViQMEJm/oSNwiCpTFOpQWWL7aOmy60knbRQ==","signatures":[{"sig":"MEUCIBFpcUC2fN6RoPnXgxpyNGDIawKjtmLfnw4ZeBi4OaJnAiEAnl6ZJbY/XFK/EBHDgvfnkbNzDed/FKSSUgpI7vSf+zA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":657016},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"6158176d03c5f5ddc0031bfe71a29753cba8b1f5","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.3.0","@daanvandenbergh/scribekit":"^1.7.2"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_1.0.1_1786466527126_0.11418906906868465","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@daanvandenbergh/backupkit","version":"1.0.2","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@1.0.2","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"78b451572f11efb405a98d19ba192f57281c30c1","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-1.0.2.tgz","fileCount":114,"integrity":"sha512-kvPU5ZxbcdogwzZc/X2Iczdj5bnu/9IHza5v2PRvBAzce+dvOpwS4j/r6jEdD7ZNSkG1N1/Wol1KbC0hVZFmiw==","signatures":[{"sig":"MEQCIBrTmTXnJ6QEZSn6r0jFKvEhBYOb1N8OwUX2jptebbfaAiAUWfd12rNn9xwNuxOYue0Th01hvSS996I4JJvnKTENNw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":657016},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"22f69da29cc6a7ed6a8e3789d730b8d88f6313a3","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.3.0","@daanvandenbergh/scribekit":"^1.9.1"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_1.0.2_1786468389380_0.20204628476114594","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@daanvandenbergh/backupkit","version":"1.0.3","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@1.0.3","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"b699208d97539dfd4dd77cc4f160648f301426b9","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-1.0.3.tgz","fileCount":114,"integrity":"sha512-S7lgCAtsMqqFUSB0VIohFFGyd9Fb2Y3sUzwNhxzE3kpdcfV8IzoFY29jmTx7MXmXYiTH77SprsrawLkYeNm+CQ==","signatures":[{"sig":"MEUCIBQ06wd9jFS3CmVetsJDbJRcqn2iIGC7/G3DCTBz3RlRAiEA8je4G5XldSynCZsGshZk50VWljWGeY7ueAtkgRZ2JEQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":684858},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"ebce4645972760ea67359f9ad9fd26f14ed6d166","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.3.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_1.0.3_1786686965220_0.15822351990000194","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@daanvandenbergh/backupkit","version":"1.0.4","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@1.0.4","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"e0da8737c126948f68ba9ca9b0366ed7ce59ff35","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-1.0.4.tgz","fileCount":114,"integrity":"sha512-SLGwPzoaFkPEa06ASZoqQAerNfXOoAx2HTiu2UtNlwr/f+WxSY8NfJm7j+zOBUhvFAgzkGUmyMvunSa29koLnQ==","signatures":[{"sig":"MEUCIQDJL5saNs11Xy1WWs4biy2o3vOc4mBvoUUBI3Tw024cVQIgaL7xKf9lr8L3x64C+IW5O0tFrHwJqHnitL7K3jd8lC0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":685274},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"f70f91846fbfac6d48a719e4917a3ae41ba2a73f","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.3.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_1.0.4_1786722047705_0.19883698371101888","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@daanvandenbergh/backupkit","version":"2.0.0","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@2.0.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"bd3220fbb32a1cf0a1eb8df15d896a1249a7602e","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-2.0.0.tgz","fileCount":116,"integrity":"sha512-GXqYlwyNr+1BoCQdNpbATIh3jc3hcDYaVloDInmq1hXHlDzhCz7i8dZol5EVlr0Oa+DKOB3Pl8SR9wt117tCpA==","signatures":[{"sig":"MEQCIHFazZfCRYT09TeEnIDWy28r2+M/NcFTQBbFHtUDDes3AiB+hq3pYhkGWPOd8CiMWuCvMQXqmUv6oLFqDDpCFYC7tQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":705256},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"9a65204e68f26c670b4bf8f47bfbc206aa191bc9","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.3.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_2.0.0_1786733582503_0.07017773846547848","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@daanvandenbergh/backupkit","version":"2.0.1","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@2.0.1","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"bc053b57607ba6095469838c9cd9179ee324c7a1","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-2.0.1.tgz","fileCount":116,"integrity":"sha512-fw9Uxp+60p8Fmm2z3GS2mUe1IUWjA/59zGXQJu7RUTAciZ1kvvvj4e2E5W5JH7orpEYLiTazWpyusIe8wAUKAA==","signatures":[{"sig":"MEYCIQDc37RS2Uv4qgKSGo4IIfxJTqH84xo+F0FwXs02ralabQIhAICDCMUY6hxTCrZH6qoKys2FDNXHLAZ+KIC+7+tg0k8P","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":706255},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"4604b9a57e3ab8d3e50c37ccaa8e9403ceedf360","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.3.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_2.0.1_1786737200734_0.3083698876145069","host":"s3://npm-registry-packages-npm-production"}},"2.0.2":{"name":"@daanvandenbergh/backupkit","version":"2.0.2","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@2.0.2","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"714b8ca3beb57147d1b39010bf5d81d2082fc5ba","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-2.0.2.tgz","fileCount":118,"integrity":"sha512-w1Oega6cbCXKj0pZsywdNWVWHD5ROHdKzVE1WtHoA/5s6TUGLpgklCVNko+s/iFHHo4Cj8+sOKlH7qFxt4yf9Q==","signatures":[{"sig":"MEYCIQCE/4hp6sijzPs0g+Xl3yol5h6jikDA+ggotlSl1M7q2wIhAPzewhaDEYdvJSOQdG76nOVhF8CzR9P8WuQawcSg1UpZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":716419},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"3b4f01c4295ff6b62232fa2f2d65ff49d9bf7fa7","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.3.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_2.0.2_1786777734184_0.18237425878053704","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@daanvandenbergh/backupkit","version":"2.1.0","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@2.1.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"7c39637794ff68cb5897d23ab3fbbe60b28b80e9","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-2.1.0.tgz","fileCount":118,"integrity":"sha512-vko3VCAmBYN2Tt1Hhc+tyy4mq3rhYqgffzFzLaHiwvz2AQ5xstPwZQ6iJ/A7wjLfR+atJSf9ZZB8kv02R/PyWQ==","signatures":[{"sig":"MEUCIQDKZC3KuatCN30mxapGzO7N5YbuYGllrSBhSrkvtcnG4AIgaLAGU7xFVuYjD3vNEJ5tj4EXhBvDQDmOf4g2NeXYXDM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":717827},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"14ee5e7b8a25a9e15292dd949b1f59239aeef3e8","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.3.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_2.1.0_1786823826750_0.4500403072700474","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"@daanvandenbergh/backupkit","version":"2.2.0","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@2.2.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"deb962a36d08fb677e35bc6411b3420153ad8cc9","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-2.2.0.tgz","fileCount":118,"integrity":"sha512-ec1fR3pJLkAUS0ycL9XgshbVPxnk7qYWfi9RNqfXcWq8fuauTrmk7fIOaglAzoE7+fmA+p1ac8hnE2GKpECDow==","signatures":[{"sig":"MEUCIQDm0uijpWs58p2MtriljB+7C6i6uE6ZcivtoRWYKZq8MwIgbsXnzGqYbseRvTyn4tnAaJztt1BK3ssm9oTCGEZrH/Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":720211},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"19b50d4e54b144733e2eeab0f34fc58aca060b1c","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.3.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_2.2.0_1786948609947_0.25030052053210095","host":"s3://npm-registry-packages-npm-production"}},"2.2.1":{"name":"@daanvandenbergh/backupkit","version":"2.2.1","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@2.2.1","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"7f016a9372a3e67ae88970dd075ef7e51c7873a2","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-2.2.1.tgz","fileCount":118,"integrity":"sha512-uxoVHx4ksKtU6j/tSoIh/aNYe/O4VsssdSek09pgzQHGVxINWB4y/ztmGaPm5H1cO/reZh//wzRgUiunlAa87w==","signatures":[{"sig":"MEUCIQD9w1caUk8ckCBubdPuD8gAR3eB1vXUJ9zscbvhzDcZCgIgW3UksVqdGGC1ZXPM4r20iI6ZIvt4z0t7kxIej1Fk174=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":720862},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"e9745b496f365beff4e8d4607cbd9d0d7725ad8e","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.4.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_2.2.1_1787078301306_0.37869756328565995","host":"s3://npm-registry-packages-npm-production"}},"2.3.0":{"name":"@daanvandenbergh/backupkit","version":"2.3.0","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@2.3.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"dd81c9eb200483ff55bb9c590fb999c853a02d64","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-2.3.0.tgz","fileCount":124,"integrity":"sha512-fVn5Z37T0m89mvNCehRu3D9sqZ6KuJrP4Rg1NFeYmggPCEadbvflIt/guqDIm9IV5e/Ahx22/MGchDGF+1VOvw==","signatures":[{"sig":"MEUCIQChk3YT6Yzjh1B+5gDsYszqyzTC2zAsG6TdaSRfPVmXoAIgdKs4ferBoZdNhNK6PbCz5G6A23KRz04KzA0XcNd0Jv8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":831534},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"62f6b85ed48aaa73fb7998da64cb098e63f5a097","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.4.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_2.3.0_1787494663105_0.013925669153864284","host":"s3://npm-registry-packages-npm-production"}},"2.4.0":{"name":"@daanvandenbergh/backupkit","version":"2.4.0","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@2.4.0","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"e9498d42e184c05789460012863ce60b1aaa3238","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-2.4.0.tgz","fileCount":124,"integrity":"sha512-l9W4O70IQNI9u7otW0aIn077fzEXEzR+lTqcwGsdnnT5K8jwMazSEMoTlLJbG2bMgMyPAsgUOCQVmYFaos1TqA==","signatures":[{"sig":"MEYCIQDpQM3M/qlHF7Xi75PTJJ2qES1QjbZySrYyqtPj8vU6QwIhAKiGEBuKmRe75AwWLSl0Lo8ZG4kLlIz7NwWWwzHvRVmq","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":865471},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"a1368f6e76b40dd72c30c72f06bea68b51a5d832","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.4.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_2.4.0_1787639648732_0.812202869330229","host":"s3://npm-registry-packages-npm-production"}},"2.4.1":{"name":"@daanvandenbergh/backupkit","version":"2.4.1","license":"UNLICENSED","_id":"@daanvandenbergh/backupkit@2.4.1","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"bin":{"backupkit":"dist/cli/main.js"},"dist":{"shasum":"8fceca02d84dd5905a7c93cca6db90f6e94fd8e7","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-2.4.1.tgz","fileCount":124,"integrity":"sha512-RdLMfdqoFSRTk/qYJGhyq4cOjqMCkYXZRfxQi2s/TXDQMkirDfrJUbHH4oj3P4NetCecBbUf7+fqvpw7xTGSMQ==","signatures":[{"sig":"MEYCIQCwcQdTiGyTwLtF9EoPWhChBF6X1mnqPhCZUQpDmRGjkwIhAJAycax2qhUlWXt/CSAiY91/Qr4giApAynQoymk+jox3","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":865471},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"4cf3d3df308a8737f8a17fabce02543523b3b058","scripts":{"docs":"npm run build && npm --prefix docs run dev","test":"vitest run --passWithNoTests","build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","typecheck":"tsc --noEmit","docs:build":"npm run build && npm --prefix docs run build","test:watch":"vitest","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main"},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"_npmVersion":"11.12.1","description":"Backup toolkit for Node.js/TypeScript projects.","directories":{},"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.6.0","@types/node":"^22.20.1","@daanvandenbergh/claudekit":"^1.4.0","@daanvandenbergh/scribekit":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/backupkit_2.4.1_1788275242605_0.25144721959661753","host":"s3://npm-registry-packages-npm-production"}},"2.5.0":{"name":"@daanvandenbergh/backupkit","version":"2.5.0","description":"Backup toolkit for Node.js/TypeScript projects.","license":"UNLICENSED","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"bin":{"backupkit":"dist/cli/main.js"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');for(const f of ['ssh/internal/askpass.sh','snapshots/internal/backupkit-remote.sh']){fs.mkdirSync('dist/'+require('node:path').dirname(f),{recursive:true});fs.copyFileSync('src/'+f,'dist/'+f);}fs.chmodSync('dist/cli/main.js',0o755)\"","test":"vitest run --passWithNoTests","test:watch":"vitest","typecheck":"tsc --noEmit","prepublishOnly":"npm run build","push-and-publish":"npm run build && git add -A && git commit -m 'Updates' && npm version patch && npm publish && git push -u origin main","docs":"npm run build && npm --prefix docs run dev","docs:build":"npm run build && npm --prefix docs run build"},"engines":{"node":">=20"},"devDependencies":{"@daanvandenbergh/claudekit":"^1.4.1","@daanvandenbergh/scribekit":"^2.0.0","@types/node":"^22.20.1","typescript":"^5.6.0","vitest":"^3.0.0"},"gitHead":"4c964454c4e6a636aae0d389df7a0e5852f47652","_id":"@daanvandenbergh/backupkit@2.5.0","_nodeVersion":"26.0.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-44qItWVooDLiPSfPXDTs4hel6f2rQHSgmCUyKQnwRjkOSH+THb3/7Q0kOEcb/52DyH/S16ziZMBOlZLurAnlEA==","shasum":"176974c7c078cfa01e0b32c615d7bcaae10aac71","tarball":"https://registry.npmjs.org/@daanvandenbergh/backupkit/-/backupkit-2.5.0.tgz","fileCount":126,"unpackedSize":874884,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCjIkr2NckU8yydCw5E8/sSRh+xe8rvHomAnpfORDTHFgIgX+8MQ5iF1g49OARPBx5dzI2V754LBgQef+NVg75i81Y="}]},"_npmUser":{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"},"directories":{},"maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/backupkit_2.5.0_1788717752369_0.7375569836606206"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-11T16:40:27.534Z","modified":"2026-09-06T18:02:32.685Z","1.0.0":"2026-08-11T16:40:27.887Z","1.0.1":"2026-08-11T16:42:07.305Z","1.0.2":"2026-08-11T17:13:09.571Z","1.0.3":"2026-08-14T05:56:05.374Z","1.0.4":"2026-08-14T15:40:47.932Z","2.0.0":"2026-08-14T18:53:02.897Z","2.0.1":"2026-08-14T19:53:20.941Z","2.0.2":"2026-08-15T07:08:54.340Z","2.1.0":"2026-08-15T19:57:06.911Z","2.2.0":"2026-08-17T06:36:50.083Z","2.2.1":"2026-08-18T18:38:21.452Z","2.3.0":"2026-08-23T14:17:43.268Z","2.4.0":"2026-08-25T06:34:08.867Z","2.4.1":"2026-09-01T15:07:22.745Z","2.5.0":"2026-09-06T18:02:32.505Z"},"license":"UNLICENSED","description":"Backup toolkit for Node.js/TypeScript projects.","maintainers":[{"name":"daanvandenbergh","email":"d.vandenbergh2@gmail.com"}],"readme":"# @daanvandenbergh/backupkit\n\n![backupkit - automated, versioned rsync backups over SSH](.agentstore/scribekit-hero/readme/hero.png)\n\nAutomated, versioned backups over SSH - a thin, dependency-free TypeScript layer on top of `rsync`.\n\n- **Versioned, or in place** - every target states its `mode`: `\"snapshot\"` makes each run a dated snapshot with unchanged files hard-linked against the previous one, so history costs almost nothing; `\"mirror\"` keeps one in-place copy of the source, for two clones of the same tree rather than an archive. Required on every target, with no default - the two have opposite recovery properties.\n- **Push or pull** - the source host can push to the backup server, or the backup server can pull from the source. Pull is the safe default: a compromised source host holds no credentials to reach the archive.\n- **Automated** - a scheduler runs each target on its own interval and prunes old snapshots on a GFS retention policy, under systemd or launchd.\n- **Safe by construction** - snapshots are atomic (write to `<name>.partial`, rename on success), locked per destination, and only complete snapshots ever seed the next hard-link base. rsync and ssh are always spawned as argv arrays, never a shell string.\n\nFull documentation: **https://daanvandenbergh.github.io/backupkit/**\n\n## Install\n\n```bash\nnpm install -g @daanvandenbergh/backupkit\n```\n\nRequires **rsync >= 3.2.5** and **ssh** on both hosts (macOS ships openrsync - `brew install rsync`). Node >= 20.\n\n## Quick start\n\nThree steps get a machine backing up:\n\n```bash\nbackupkit init             # write a commented starter config.jsonc\nbackupkit check            # verify rsync/ssh versions, keys, and host reachability\nbackupkit start            # schedule it in this session (Ctrl-C stops it)\n```\n\nThe last step is where the two run modes fork: `backupkit start` schedules in *your* session, `sudo backupkit service install` registers a root service instead. `check` ends by printing whichever of the two your config can use - a passphrase-protected key rules the service out - with the `--config` path already filled in. See [Two ways to run it](#two-ways-to-run-it).\n\nA minimal **pull** config (`/etc/backupkit/config.jsonc`) - the backup server fetches from a source host that holds no archive credentials:\n\n```jsonc\n{\n    \"remotes\": {\n        // A remote is either explicit (host/user/identityFile) or an ssh_config alias ({ \"alias\": \"...\" }).\n        \"web1\": {\n            \"host\": \"10.0.0.11\",\n            \"user\": \"backup-reader\",\n            \"identityFile\": \"/etc/backupkit/keys/web1_ed25519\"\n        }\n    },\n    \"targets\": {\n        \"web1-www\": {\n            \"mode\": \"snapshot\",            // versioned archive (the alternative is \"mirror\")\n            \"direction\": \"pull\",           // this host fetches from the remote\n            \"remote\": \"web1\",\n            \"source\": \"/var/www\",          // path on the remote\n            \"destination\": \"/srv/backups/web1-www\", // this target's archive -> /srv/backups/web1-www/<snapshot>/\n            \"schedule\": { \"interval\": \"day\", \"intervalCount\": 1, \"at\": \"03:00\" },\n            \"retention\": { \"keepDaily\": 14, \"keepWeekly\": 8, \"keepMonthly\": 12 }\n        }\n    }\n}\n```\n\n**`destination` is that target's own directory**, and it means the same thing in both modes: a snapshot target writes `<destination>/<snapshot>/` into it, a mirror writes `<destination>` itself. Backupkit never appends the target name, so give every target a destination of its own - two targets pointed at one directory is a config error, named as such.\n\nThen `backupkit run` performs one pass over every due target; `backupkit start` (or the installed service) does it on schedule.\n\n> **Upgrading from 1.x.** `destination` used to be a *shared parent* that backupkit appended the target name to, so `\"destination\": \"/srv/backups\"` for target `web1-www` wrote to `/srv/backups/web1-www/<snapshot>/`. It is now the archive root itself. Nothing on disk has to move - **append the target name to each snapshot target's `destination`** (`/srv/backups` -> `/srv/backups/web1-www`) and the existing archive is exactly where 2.x expects it. `backupkit check` finds a local archive left one level down and prints the destination to set. Push targets also need `sudo backupkit jail install` rerun on the archive server: the 2.x client sends a path an older jail rejects, so every push fails until it is updated. Mirror targets are unaffected.\n\n### Snapshot or mirror\n\n`mode` is required on every target and has no default, because the two modes have opposite properties and a silent one is the wrong one to inherit:\n\n```jsonc\n\"persistance\": {\n    \"mode\": \"mirror\",                     // <destination> IS the tree - one copy, no history\n    \"direction\": \"pull\",                  // this host fetches; the source needs no access here\n    \"remote\": \"laptop\",\n    \"source\": \"/Users/me/persistance\",\n    \"destination\": \"/Volumes/persistance\",\n    \"schedule\": { \"interval\": \"hour\" }\n}\n```\n\nA **mirror** keeps two clones of one directory in step - a working directory and its copy on another machine or an external volume. It writes the destination in place, so there is no snapshot subdirectory, no hard-link history, no retention (`retention` and `minFree` are config errors on a mirror rather than silent no-ops), and nothing for `list`, `prune`, or `restore` to work with. **Anything the source no longer has is deleted from the destination on the next run, with no earlier version to go back to** - that is the trade against a snapshot target.\n\nThe one guard that stays is the one a mirror cannot recover from without: before each run backupkit estimates the source, and refuses the transfer if it holds under half the files the last successful run moved, leaving the destination untouched. An unmounted volume or a half-restored directory therefore skips its run instead of deleting the good copy; `backupkit run --force <target>` performs it once you have checked. A push mirror must also set `\"jail\": false` - the jail pins every transfer to a scratch `<snapshot>.partial`, which is what confines the deletes it allows, so it cannot accept a mirror; prefer `\"direction\": \"pull\"` for a mirror wherever you can.\n\n## CLI\n\n| Command | What it does |\n|---|---|\n| `backupkit init` | Write a commented starter `config.jsonc` at the resolved path. |\n| `backupkit check` | Validate config; verify rsync/ssh versions on both ends, probe each host, pin host keys (on a TTY), print the recommended push-jail `authorized_keys` lines (informational - the jail is never probed). |\n| `backupkit run [TARGET...]` | Back up due targets now (`--force` ignores the schedule, `--dry-run` transfers nothing). |\n| `backupkit status [TARGET...]` | One row per target: last run, next due, consecutive failures, lock state (`--json`). |\n| `backupkit list [TARGET...]` | Complete snapshots, oldest first (alias `ls`, `--json`). |\n| `backupkit target [TARGET]` | Dump one target's settings as backupkit resolved them - every default filled in; no name lists every target (`--json`). |\n| `backupkit remote [REMOTE]` | Dump one remote's settings as backupkit resolved them, and the targets using it; no name lists every remote (`--json`). |\n| `backupkit restore TARGET SNAP` | Copy a snapshot (`SNAP` may be `latest`) to a fresh `--output` path; never overwrites, never deletes. `--dry-run` previews it. |\n| `backupkit prune [TARGET...]` | Apply retention now (`--dry-run` prints the keep/prune plan; `--force` overrides the planted-snapshot guard). |\n| `backupkit unlock [TARGET...]` | Clear a destination lock backupkit cannot retake itself - one left by another machine. A live lock is reported, not removed, unless `--force`. |\n| `backupkit service <verb>` | `install`/`uninstall`/`start`/`stop`/`restart`/`status` the systemd unit or launchd job. |\n| `backupkit logs [-f] [-n N]` | Tail the daemon logs (journald on Linux, log files on macOS). |\n| `backupkit start` | Foreground scheduler loop **in your own session**: starts an ssh-agent, adds every key (prompting for each passphrase), then schedules until Ctrl-C. `--force` runs every target once first. The supported home for passphrase-protected keys. |\n| `backupkit daemon` | Foreground scheduler loop (what the installed service runs). Refuses to start if any configured key is passphrase-protected - a service has no terminal to unlock one on. |\n| `backupkit jail install\\|status` | Run **on an archive server** (config-free): atomically install/update the shipped `backupkit-remote` jail script at `/usr/local/bin/backupkit-remote`, or verify the installed copy matches this package version. |\n\n`--config <path>` overrides the default lookup (`$BACKUPKIT_CONFIG`, then `/etc/backupkit/config.jsonc`, then `~/.backupkit/config.jsonc`). `init` writes at that same resolved path - `/etc/backupkit/` under `sudo`, `~/.backupkit/` as yourself - and since the daemon runs as root, a config kept in your home directory has to be named at install time: `sudo backupkit service install --config ~/.backupkit/config.jsonc`. See the [configuration reference](https://daanvandenbergh.github.io/backupkit/configuration) and [CLI reference](https://daanvandenbergh.github.io/backupkit/cli-reference).\n\n## Two ways to run it\n\nThe same scheduler - same windows, same retention, same locking - runs in one of two modes. They differ in who supervises it, which config it reads, and whether a key may have a passphrase.\n\n| | [Run it yourself](https://daanvandenbergh.github.io/backupkit/run-it-yourself) | [Run it as a service](https://daanvandenbergh.github.io/backupkit/daemon-setup) |\n|---|---|---|\n| Command | `backupkit start` | `sudo backupkit service install` + `start` |\n| Runs as | you, in a terminal | root, under systemd/launchd |\n| Config | `~/.backupkit/config.jsonc` | `/etc/backupkit/config.jsonc` |\n| `stateDir` default | `~/.local/state/backupkit` | `/var/lib/backupkit` (`/var/db/backupkit` on macOS) |\n| Passphrase-protected key | **yes**, prompts once per key | **no**, refuses to start |\n| Survives logout/reboot | no | yes |\n| Logs | this terminal (plus `logging.file`) | journald / `/var/log/backupkit`, via `backupkit logs` |\n\n`backupkit daemon` - the loop the installed service runs - checks every configured key before it starts anything and **exits with an error if any of them is passphrase-protected**, naming the key and its remote. A launchd job or systemd unit has no terminal to type a passphrase on, and putting the passphrase in a file beside the key buys no secrecy while adding a second secret to lose. A key counts as protected when its remote declares a `passphrase` *or* when the key file itself turns out to be encrypted.\n\nSo either give the service its own key with no passphrase (`ssh-keygen -t ed25519 -N \"\"`), or run the scheduler yourself:\n\n```bash\nbackupkit start   # ssh-agent + prompt once per key, then schedule until Ctrl-C\n```\n\nAlias remotes sit outside all of this: `ssh_config` and an agent are in charge of those, though a root service resolves them against *root's* ssh_config, not yours.\n\n## Push vs pull\n\n**Pull** (recommended) keeps every credential on the backup server: it reaches out and fetches, so a compromised source host holds nothing that can read or delete a stored snapshot. **Push** is for when the source cannot be reached inbound; by default the push key on the archive server is confined by a `backupkit-remote` forced command that jails it to **one target's archive root** - its `destination`, so a key that backs up one target cannot reach another's archive at all - permits only rsync plus the snapshot lifecycle, pins each transfer's destination to the scratch `<snapshot>.partial` directory of the run, and validates a rename as a *pair* so a snapshot can never be renamed into a deletable name it did not already have - so no single command, and no pair of commands, can erase a target's archive history. `backupkit check` prints the exact `authorized_keys` line, and installing the script on the archive server is one command there: `npm install -g @daanvandenbergh/backupkit && sudo backupkit jail install` (rerun `jail install` after every package update; `backupkit jail status` tells you when the installed copy has fallen behind, and every `backupkit` command on that server warns on stderr while an installed copy is outdated). Installation is deliberately the server admin's local act - the push client never holds a credential that could rewrite the jail that confines it.\n\nThe jail is **recommended, not required**: set `\"jail\": false` on a push target to run without it - for a plain account whose risk you accept, or a restricted host (a storage-box style appliance) where a forced command cannot be installed. Transfers are identical either way: the client always speaks plain rsync plus POSIX file commands over ssh, and the jail only *filters* them, so nothing is installed on the happy path that the unjailed path lacks. What changes is authority: an unjailed push key can do whatever the account allows, including deleting the entire archive. Without the jail the account must accept these commands over ssh: `rsync --server`, `mkdir -p --`, `mkdir --`, `mv --`, `ls -A --`, `rm -rf --`, and `df -Pk --` - listing is the one command that differs by mode (a jailed target lists with the jail's `find <dir> -maxdepth 1 -mindepth 1 -print0`), because a restricted appliance shell often has no `find` at all. `backupkit check` never probes for the jail; it only prints the lines for jailed targets and notes each `\"jail\": false` one. An appliance whose shell parses no quoting at all - a Hetzner Storage Box reads `'mkdir'` as a command named `'mkdir'` - additionally needs `\"restrictedShell\": true` on the remote: backupkit then sends each command as bare words and refuses to send any element that is not already one inert shell word. Those bare words are exactly what the jail's quoted grammar rejects, so a `restrictedShell` remote's push targets must also set `\"jail\": false` - the config validator refuses the pair rather than letting it fail at run time.\n\nWhat the jail **cannot** prevent is the authority push mode inherently grants: the pushing host runs retention, so a compromised push client can retire completed snapshots one at a time exactly as the legitimate client does. The archive server holds no retention policy of its own and cannot tell a policy-driven prune from a targeted deletion. That is the structural reason pull is the recommendation, and it is why `backupkit prune` is the only bulk path.\n\nWhat neither direction can prevent is a source that lies about its own contents: a compromised source can present an emptied tree, and count-based retention would eventually age out the snapshots holding the real data. Backupkit trips a content-collapse guard when a snapshot's file count collapses against the previous one - the snapshot is still stored, but retention is skipped and the run is logged at error level until you confirm the shrink is real with `backupkit prune`. See the [security model](https://daanvandenbergh.github.io/backupkit/security-model).\n\n## Library API\n\nThe CLI is a thin shell over the `Backupkit` class, which you can drive directly:\n\n```ts\nimport { Backupkit } from \"@daanvandenbergh/backupkit\";\n\nconst kit = Backupkit.fromConfig(); // or Backupkit.fromConfig(\"/etc/backupkit/config.jsonc\")\n\nawait kit.check();                       // readiness: binaries, keys, hosts, jail lines\nawait kit.run();                         // one pass over every due target\nawait kit.start();                       // foreground scheduler loop (stop() ends it)\n\nconst rows = await kit.status();         // last run, next due, failures, lock state\nconst snaps = await kit.listSnapshots(); // complete snapshots, oldest first\nawait kit.prune({ dryRun: true });       // retention plan without deleting\nawait kit.restore({ target: \"web1-www\", snapshot: \"latest\", output: \"/tmp/restored\" });\n```\n\n## License\n\nUNLICENSED.\n","readmeFilename":"README.md"}