72900049ddaf

rename clone, push and commit out of Kepr's source, tests and import script: a repo is stolen, a copy follows its source, the import ends its transaction with END (v3.0.85)

time
2026-09-28 12:47 UTC · 2 weeks ago
machine
Junes-MacBook-Pro.local
agent
claude-opus-5-5
session
remove git terminology from source
parent
223dda02aa56
8 files package.json+1 −1scripts/import-kepr2.ts+2 −2src/mirror/remote.ts+1 −1src/server/connect.ts+1 −1tests/connect.test.ts+1 −1tests/kohremote.test.ts+1 −1tests/kohsteal.test.ts+30 −30tests/kohupdate.test.ts+1 −1
package.json+1 −1
@@ -1,6 +1,6 @@11 {22 	"name": "kepr",3-	"version": "3.0.84",3+	"version": "3.0.85",44 	"private": true,55 	"type": "module",66 	"scripts": {

view file

scripts/import-kepr2.ts+2 −2
@@ -193,9 +193,9 @@193193 		errors.push(`${prefix}: ${error instanceof Error ? error.message : error}`);194194 	}195195 }196-// Fail before any write (kepr.db is uncommitted); writes never overwrite, so a re-run finishes one.196+// Fail before any write (kepr.db is not yet written); writes never overwrite, so a re-run finishes one.197197 if (errors.length > 0) throw new Error(`not imported, nothing written:\n${errors.join("\n")}`);198198 for (const [repo, version] of newest) writes.push([`${repo}/releases/latest`, version]);199199 for (const [key, body] of writes) await put(key, body);200-kepr.run(dry ? "ROLLBACK" : "COMMIT");200+kepr.run(dry ? "ROLLBACK" : "END");201201 await pg.close();

view file

src/mirror/remote.ts+1 −1
@@ -132,7 +132,7 @@132132 		}),133133 	);134134 135-	// Koh lists `<repo>/saves` and `<repo>/objects` as well as its events: a clone walks the chain135+	// Koh lists `<repo>/saves` and `<repo>/objects` as well as its events: a steal walks the chain136136 	// it is given here, and fetches every object those saves name. Both lists follow the mirror, so137137 	// neither is cached.138138 	app.get("/:repo/koh/saves", (c) =>

view file

src/server/connect.ts+1 −1
@@ -112,7 +112,7 @@112112 		const address = createHash("sha256").update(attemptKey).update(clientIp(c)).digest("hex");113113 		const used = attempts.filter((attempt) => attempt.address === address).length;114114 		// A refused attempt is not recorded: that caps the list at ten entries per address, and keeps115-		// hammering from pushing the block out past the hour.115+		// hammering from moving the block out past the hour.116116 		if (used >= ATTEMPTS_PER_ADDRESS) return noCode(c);117117 		attempts.push({ at: now, address });118118 		let code = "";

view file

tests/connect.test.ts+1 −1
@@ -266,7 +266,7 @@266266 		const address = "192.0.2.90";267267 		for (let i = 0; i < 10; i++) await redeem(app, "bad", address);268268 		// Fifty more inside the hour are all refused, and none of them is remembered: kept, they would269-		// cost memory per request and push the block out an hour past the last one.269+		// cost memory per request and move the block out an hour past the last one.270270 		clock += 3_540_000;271271 		setSystemTime(clock);272272 		for (let i = 0; i < 50; i++) expect((await redeem(app, "bad", address)).status).toBe(404);

view file

tests/kohremote.test.ts+1 −1
@@ -78,7 +78,7 @@7878 		expect((await inst.app.request(`/${REPO}/saves/${checkpoint}`)).status).toBe(404);7979 	});8080 81-	test("the save and object prefixes list what a clone walks", async () => {81+	test("the save and object prefixes list what a steal walks", async () => {8282 		const saves = await get("saves");8383 		expect(saves.status).toBe(200);8484 		expect(saves.headers.get("cache-control")).toBe("no-cache");

view file

tests/kohsteal.test.ts+30 −30
@@ -73,14 +73,14 @@7373 	return { code, out, err };7474 }7575 76-/** The whole line, every field of every save: what a clone must reproduce exactly. */76+/** The whole line, every field of every save: what a stolen copy must reproduce exactly. */7777 async function line(repo: string, home: string): Promise<unknown> {7878 	return JSON.parse((await koh3(repo, home, ["view", "--json"])).out).saves;7979 }8080 8181 describe.serial("stealing a public repo from Kepr over HTTP", () => {8282 	test(83-		"a clone comes over the wire, stays read-only, and follows its source",83+		"a stolen copy comes over the wire, stays read-only, and follows its source",8484 		async () => {8585 			const root = await mkdtemp(join(tmpdir(), "kepr-steal-"));8686 			const bucketServer = await startBucket();@@ -156,15 +156,15 @@156156 				await mkdir(thiefDir, { recursive: true });157157 				const stolen = await koh3(thiefDir, thiefHome, ["steal", `${host}/${REPO}`]);158158 				expect(stolen.code, stolen.err + stolen.out).toBe(0);159-				const clone = join(thiefDir, REPO);160-				expect(await readFile(join(clone, "README.md"), "utf8")).toContain("edited");161-				expect((await koh3(clone, thiefHome, ["doctor"])).code).toBe(0);162-				// Save for save, the clone is the line the owner published.163-				expect(await line(clone, thiefHome)).toEqual(await line(repoDir, ownerHome));159+				const copy = join(thiefDir, REPO);160+				expect(await readFile(join(copy, "README.md"), "utf8")).toContain("edited");161+				expect((await koh3(copy, thiefHome, ["doctor"])).code).toBe(0);162+				// Save for save, the copy is the line the owner published.163+				expect(await line(copy, thiefHome)).toEqual(await line(repoDir, ownerHome));164164 				// The source is remembered, and shown above the machine's remote.165-				expect((await koh3(clone, thiefHome, ["remote"])).out).toContain(`${host}/${REPO}`);165+				expect((await koh3(copy, thiefHome, ["remote"])).out).toContain(`${host}/${REPO}`);166166 				// And it cannot be offered: it has no bucket of its own.167-				const refused = await koh3(clone, thiefHome, ["offer"]);167+				const refused = await koh3(copy, thiefHome, ["offer"]);168168 				expect(refused.code).toBe(1);169169 				expect(refused.err + refused.out).toContain("which is read-only");170170 @@ -172,11 +172,11 @@172172 				await writeFile(join(repoDir, "README.md"), "# shipped\n\nA third save.\n");173173 				expect((await koh3(repoDir, ownerHome, ["save", "third save"])).code).toBe(0);174174 				await sync();175-				expect((await koh3(clone, thiefHome, ["steal"])).code).toBe(0);176-				expect(await readFile(join(clone, "README.md"), "utf8")).toContain("A third save");175+				expect((await koh3(copy, thiefHome, ["steal"])).code).toBe(0);176+				expect(await readFile(join(copy, "README.md"), "utf8")).toContain("A third save");177177 178178 				// A strip on the source side reaches the mirror, and is honoured: history no longer179-				// holds the path, so a fresh clone never sees the file at all. On the machine that179+				// holds the path, so a fresh steal never sees the file at all. On the machine that180180 				// already had it the file stays on disk — a strip is not a delete — but it is banned:181181 				// not unsaved work, and never saved again.182182 				await stripPath(repoDir, ownerHome, "src.v");@@ -186,10 +186,10 @@186186 					0,187187 				);188188 				await sync();189-				expect((await koh3(clone, thiefHome, ["steal"])).code).toBe(0);190-				expect((await koh3(clone, thiefHome, ["doctor"])).code).toBe(0);191-				expect(await Bun.file(join(clone, "src.v")).exists()).toBe(true);192-				expect((await koh3(clone, thiefHome, ["status"])).out).not.toContain("src.v");189+				expect((await koh3(copy, thiefHome, ["steal"])).code).toBe(0);190+				expect((await koh3(copy, thiefHome, ["doctor"])).code).toBe(0);191+				expect(await Bun.file(join(copy, "src.v")).exists()).toBe(true);192+				expect((await koh3(copy, thiefHome, ["status"])).out).not.toContain("src.v");193193 				const fresh = join(root, "thief-again");194194 				await mkdir(fresh, { recursive: true });195195 				expect((await koh3(fresh, thiefHome, ["steal", `${host}/${REPO}`])).code).toBe(0);@@ -222,53 +222,53 @@222222 223223 				// A save made before the upgrade is local work and nothing else: it must not write to224224 				// the bucket, and it must not quietly drop the source on its own.225-				await writeFile(join(clone, "local.txt"), "work in progress\n");226-				const local = await koh3(clone, thiefHome, ["save", "local only"]);225+				await writeFile(join(copy, "local.txt"), "work in progress\n");226+				const local = await koh3(copy, thiefHome, ["save", "local only"]);227227 				expect(local.code, local.err + local.out).toBe(0);228228 				expect(local.out + local.err).not.toContain("offered");229-				expect((await koh3(clone, thiefHome, ["remote"])).out).toContain(`${host}/${REPO}`);229+				expect((await koh3(copy, thiefHome, ["remote"])).out).toContain(`${host}/${REPO}`);230230 231231 				// The next offer drops the source: the repo is this machine's own work now; offer is232232 				// the path the addendum names for the owner case.233-				const upgraded = await koh3(clone, thiefHome, ["offer"]);233+				const upgraded = await koh3(copy, thiefHome, ["offer"]);234234 				expect(upgraded.code, upgraded.err + upgraded.out).toBe(0);235235 				expect(upgraded.out + upgraded.err).toContain("now syncs with your bucket");236236 				// The owner can read what that offer published, before anything else is saved: the237-				// local save the clone was carrying went up with it.237+				// local save the copy was carrying went up with it.238238 				expect((await koh3(repoDir, ownerHome, ["revert", "--yes"])).code).toBe(0);239239 				expect((await koh3(repoDir, ownerHome, ["steal"])).code).toBe(0);240240 				expect(await readFile(join(repoDir, "local.txt"), "utf8")).toContain("work in progress");241241 				expect(JSON.parse((await koh3(repoDir, ownerHome, ["view", "--json"])).out).saves).toEqual(242-					JSON.parse((await koh3(clone, thiefHome, ["view", "--json"])).out).saves,242+					JSON.parse((await koh3(copy, thiefHome, ["view", "--json"])).out).saves,243243 				);244-				await writeFile(join(clone, "README.md"), "# shipped\n\nThe thief's edit.\n");245-				expect((await koh3(clone, thiefHome, ["save", "from the thief"])).code).toBe(0);244+				await writeFile(join(copy, "README.md"), "# shipped\n\nThe thief's edit.\n");245+				expect((await koh3(copy, thiefHome, ["save", "from the thief"])).code).toBe(0);246246 				expect((await koh3(repoDir, ownerHome, ["steal"])).code).toBe(0);247247 				expect(await readFile(join(repoDir, "README.md"), "utf8")).toContain("The thief's edit");248248 249-				// The other clone still rides its source. When both sides changed the same file,249+				// The other copy still rides its source. When both sides changed the same file,250250 				// offer without a terminal refuses as steal would, and never picks a side itself:251251 				// the file is named, steal is where to choose, and the source stays.252252 				const other = join(fresh, REPO);253253 				await writeFile(join(repoDir, "README.md"), "# shipped\n\nThe owner's edit.\n");254254 				expect((await koh3(repoDir, ownerHome, ["save", "owner edits the readme"])).code).toBe(0);255-				await writeFile(join(other, "README.md"), "# shipped\n\nThe other clone's edit.\n");256-				expect((await koh3(other, thiefHome, ["save", "other clone edits"])).code).toBe(0);255+				await writeFile(join(other, "README.md"), "# shipped\n\nThe other copy's edit.\n");256+				expect((await koh3(other, thiefHome, ["save", "other copy edits"])).code).toBe(0);257257 				const clashed = await koh3(other, thiefHome, ["offer"]);258258 				expect(clashed.code).toBe(1);259259 				expect(clashed.err).toContain(260260 					'README.md changed on both machines — run "koh steal" to choose, then offer',261261 				);262-				expect(await readFile(join(other, "README.md"), "utf8")).toContain("other clone's edit");262+				expect(await readFile(join(other, "README.md"), "utf8")).toContain("other copy's edit");263263 				expect((await koh3(other, thiefHome, ["remote"])).out).toContain(`${host}/${REPO}`);264-				// Choosing there upgrades the clone, and says where the version it gave up lives.264+				// Choosing there upgrades the copy, and says where the version it gave up lives.265265 				const chosen = await koh3(other, thiefHome, ["steal", "--theirs"]);266266 				expect(chosen.code, chosen.err + chosen.out).toBe(0);267267 				expect(chosen.out + chosen.err).toContain("now syncs with your bucket");268268 				expect(chosen.out + chosen.err).toContain("your README.md is in");269269 				expect(await readFile(join(other, "README.md"), "utf8")).toContain("The owner's edit");270270 271-				// A repo the public side never serves clones from the bucket once signed in.271+				// A repo the public side never serves is stolen from the bucket once signed in.272272 				const second = join(root, "thief-two");273273 				await mkdir(second, { recursive: true });274274 				const kept = await koh3(second, thiefHome, ["steal", `${host}/${PRIVATE_REPO}`]);

view file

tests/kohupdate.test.ts+1 −1
@@ -250,7 +250,7 @@250250 		const downloaded = new Uint8Array(await Bun.file(installed).arrayBuffer());251251 		const released = new Uint8Array(await Bun.file(published).arrayBuffer());252252 		if (process.platform === "darwin") {253-			// Apple silicon refuses to run an unsigned binary, so koh signs the staged253+			// Apple silicon refuses to run an unsigned binary, so koh signs the new254254 			// file before the rename: the installed file is the artifact with a fresh255255 			// signature, and its size may go either way, so the bytes cannot be compared256256 			// here. What can be proved is that the file that landed is a validly signed,

view file