Skip to content

Fix setup.sh: match real KV title and build pages before deploy - #1

Open
ceyniustranberg wants to merge 1 commit into
daniloc:mainfrom
ceyniustranberg:fix/setup-kv-title-and-page-build
Open

Fix setup.sh: match real KV title and build pages before deploy#1
ceyniustranberg wants to merge 1 commit into
daniloc:mainfrom
ceyniustranberg:fix/setup-kv-title-and-page-build

Conversation

@ceyniustranberg

Copy link
Copy Markdown

Two first-run bugs in scripts/setup.sh, hit during a fresh deploy.

1. KV namespace title mismatch

The find-or-create step looked for a namespace titled ${WORKER_NAME}-OAUTH_KV (mnemion-OAUTH_KV), but wrangler kv namespace create OAUTH_KV titles the namespace after the binding nameOAUTH_KV. So the lookup never matched an existing namespace, setup fell through to create it, and the create failed on A KV namespace with the title "OAUTH_KV" already exists, aborting setup on any re-run.

Fix: match the title wrangler actually produces (OAUTH_KV).

2. Missing page build before deploy

Setup runs wrangler deploy directly, but the worker imports compiled SSR/client bundles from dist/** (wired via [[rules]] in wrangler.toml). Those only exist after npm run build:pages (which npm run deploy runs, but setup.sh does not). On a clean checkout the deploy fails on unresolved imports like Could not resolve "../../dist/server/entry-server.mjs".

Fix: run npm run build:pages before wrangler deploy in setup.

Testing

  • bash -n scripts/setup.sh passes.
  • Verified end-to-end on a fresh account: with both fixes, setup builds, deploys, and prints the correct https://mnemion.<subdomain>.workers.dev/setup?token=... URL.

🤖 Generated with Claude Code

Two first-run bugs in scripts/setup.sh:

1. KV title mismatch — the find-or-create step looked for a namespace
   titled "${WORKER_NAME}-OAUTH_KV", but `wrangler kv namespace create
   OAUTH_KV` titles it after the binding name ("OAUTH_KV"). The lookup
   never matched, so setup tried to create it and failed on "already
   exists" on any re-run. Match the title wrangler actually produces.

2. Missing page build — setup runs `wrangler deploy` directly, but the
   worker imports compiled SSR/client bundles from dist/** that only
   exist after `npm run build:pages`. A clean checkout failed the deploy
   on unresolved imports. Build the bundles before deploying.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
daniloc added a commit that referenced this pull request Jun 21, 2026
…ind-review punch-list)

The blind production-readiness review's verdict was "ship-with-caveats" on ONE
lopsided gap: the same discipline that produced ten security totality oracles
left the system an operational black box (≈7 console calls, no retained logs, no
request-path error boundary). This closes that cluster (review findings #1,#3,#4,
#5,#6) with the lightweight Cloudflare-native primitive.

Foundation:
- wrangler.toml: enable Workers Logs ([observability] + invocation_logs) on the
  default AND test envs — the native primitive (no Logpush, no tail worker). It
  ingests console.* for 7 days and attaches per-request metadata (method/url/
  status/outcome/rayId) automatically, so code logs only the event + context.
- shared/core/log.ts: the ONE home for log SHAPE — logError/logWarn emit a single
  JSON object with a stable `event` slug (dashboard-groupable) + serialized error.

#3 Request-path error boundary (defense-in-depth, two tiers):
- index.ts handle(): top-level catch → logs `request.unhandled` (method + pathname
  ONLY, never url.search — it can carry tokens), returns a generic 500, never
  leaks internals.
- router.ts: per-route catch (await the handler) → logs `route.handler_threw` with
  route attribution. Returns its own 500, so handle()'s catch only fires outside
  routing.

#4 Symmetric write-path errors: executeMutate's existing catch now logs
`mutate.write_failed`; batchMutate wraps transactionSync so a raw DB throw returns
the SAME structured {error,message} a single mutate returns instead of escaping
raw across the RPC/MCP boundary — atomicity preserved (rollback completes inside
workerd before the catch). Regression proves a batch constraint failure returns
structured + commits zero rows.

#5 Swallowed async failures now visible (WARN, still non-fatal): Vectorize embed/
delete (prime.ts), R2 orphan-blob deletes (manifest.ts, io.ts), PDF extraction
(documents.ts) — each was a silent discard; now logged with context.

#6 Recall blackout ≠ empty hive: prime() returns an additive {degraded, reason}
marker ONLY on a genuine AI/Vectorize outage (still empty, still non-throwing);
the prime tool handler surfaces a note so an agent can tell "recall unavailable"
from "no memories". Unmarked on a legitimate empty result.

(#7 DNS-rebinding residual left as-is — already mitigated by the federation
consent allow-list + per-hop re-validation.)

574 tests (+6), ✓ coherent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqsTxzkdZV3CAtj4TA8URf
daniloc added a commit that referenced this pull request Jun 21, 2026
…gnore

Acts on external review of the public/unauthenticated surfaces.

#1 Rate limiting (the material one) — adds two GA `ratelimit` bindings (wrangler.toml,
built-in, no provisioning) enforced via a fail-open `rateLimit` guard (router.ts):
- RL_INGRESS on POST /i/:path, keyed per endpoint path — each accepted ingress write
  creates an entry + a billable Workers AI embedding + Vectorize upsert, all serialized
  through the user's single HiveDO, so this caps attacker-driven cost amplification
  regardless of source IP (60/min).
- RL_PUBLIC on /o, /p, /marketplace, keyed per client IP — caps cache-busting
  enumeration that bypasses the 60s edge cache and lands on the DO (200/min).
The guard FAILS OPEN (missing binding / limiter error → allow) so it never takes down
a deploy or test env that doesn't provide it; the 429/fail-open logic is unit-tested
(the local miniflare limiter is a no-op).

#2 Body-size caps — POST /i and the text /upload buffered request.text() with no cap
before the downstream 1 MB entry limit; added an early Content-Length reject at 4 MB
(MAX_TEXT_BODY), mirroring the 25 MB up-front cap /f document upload already had.

#4 .dev.vars — added to .gitignore (it didn't match .env*); Wrangler's real local-secret
file could have been committed. Kept .dev.vars.example tracked.

#3 (single-DO ceiling) needs no separate code: public reads are already edge-cached and
the residual write-contention is exactly what #1 throttles.

687 tests, ✓ coherent, all ratchets green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqsTxzkdZV3CAtj4TA8URf
daniloc added a commit that referenced this pull request Jun 22, 2026
First-run setup deployed via raw `wrangler deploy` with no build step, so
on a clean checkout the deploy failed on the unresolved dist/fragment
import and the missing dist/web assets dir. Build the MCP render fragment
(build:pages) and the React SPA (build:web) first, as `npm run deploy`
does. Credit to #1 (ceyniustranberg) for catching the missing build; that
PR predates the React SPA so it built only the fragment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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