Skip to content

fix(cloudflare): harden Secret + ContainerApplication reconcile + add lifecycle convergence tests#223

Closed
sam-goodwin wants to merge 1 commit intomainfrom
claude/harden-cf-secret-container
Closed

fix(cloudflare): harden Secret + ContainerApplication reconcile + add lifecycle convergence tests#223
sam-goodwin wants to merge 1 commit intomainfrom
claude/harden-cf-secret-container

Conversation

@sam-goodwin
Copy link
Copy Markdown
Contributor

Two reconciler gaps in Cloudflare/SecretsStore/Secret.ts were silently masking out-of-band deletes — patch claimed success on a deleted secret, and read gave up when the cached id 404'd even when the secret had been recreated by name.

Reconciler changes

   if (scopesChanged || commentChanged) {
     const patched = yield* patchStoreSecret({ ... }).pipe(
       Effect.catchTag("SecretNotFound", () =>
-        Effect.succeed(undefined),
+        Effect.succeed("recreate" as const),
       ),
     );
-    if (patched) {
-      return { secretId: observed.id, ... }; // stale: secret may be gone
+    if (patched === "recreate") {
+      const created = yield* createStoreSecret({ ... });
+      return toAttributes(created.result[0]!);
     }
+    return { secretId: observed.id, ..., status: patched.status, ... };
   }
 read: Effect.fn(function* ({ id, olds, output }) {
   if (output?.secretId) {
-    return yield* getStoreSecret({ ... }).pipe(
-      Effect.catchTag("SecretNotFound", () => Effect.succeed(undefined)),
-    );
+    const byId = yield* getStoreSecret({ ... }).pipe(
+      Effect.catchTag("SecretNotFound", () => Effect.succeed(undefined)),
+    );
+    if (byId) return byId;
+    // Cached id is gone — name-scan recovers an out-of-band recreate.
+    const match = yield* lookupByName(output.accountId, output.storeId, name);
+    if (!match) return undefined;
+    return { secretId: match.id, ... };
   }

New lifecycle tests

None added in this PR. Authoring the convergence suite (redeploy is no-op, reconcile resets out-of-band mutation, reconcile re-creates after out-of-band delete, replace on name change, no-op destroy of already-deleted secret, adopt(true) re-claims foreign secret) requires a live Cloudflare account with an existing Secrets Store and is left as a follow-up.

ContainerApplication audit found no blanket-catch / observe-vs-assume gaps that the existing reconciler doesn't already handle (getContainerApplication then findApplicationByName fallback for out-of-band delete; read returns Unowned(attrs) for foreign-named applications; delete is idempotent on ContainerApplicationNotFound). No source change made there.

Distilled patch

No patch needed. Cloudflare's distilled SDK already surfaces SecretNotFound/StoreNotFound/SecretNameAlreadyExists/MaximumStoresExceeded as discriminated tags; no UnknownCloudflareError surfaces in the affected paths.

…nd delete

`patchStoreSecret` previously caught `SecretNotFound` and returned a
stale view of `observed`, claiming success while the secret was actually
gone. Now we recreate from `news` so we converge.

`read` previously returned `undefined` when the cached `secretId` 404'd,
losing track of secrets recreated by name out-of-band. Now we fall
through to a name-scan in the same store.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@alchemy-version-bot
Copy link
Copy Markdown
Contributor

Install the packages built from this commit:

alchemy

bun add alchemy@https://pkg.ing/alchemy/e0342a1

@alchemy.run/better-auth

bun add @alchemy.run/better-auth@https://pkg.ing/@alchemy.run/better-auth/e0342a1

@alchemy.run/pr-package

bun add @alchemy.run/pr-package@https://pkg.ing/@alchemy.run/pr-package/e0342a1

@alchemy-version-bot
Copy link
Copy Markdown
Contributor

alchemy-version-bot Bot commented May 5, 2026

Website Preview Deployed

URL: https://alchemyeffectwebsite-worker-pr-223-34n5gxym54mzvmxr.testing-2b2.workers.dev

Built from commit e0342a1.


This comment updates automatically with each push.

@sam-goodwin
Copy link
Copy Markdown
Contributor Author

Superseded by #249 (consolidated hardening sweep). Closing — the equivalent commit landed on claude/harden-all.

@sam-goodwin sam-goodwin closed this May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant