LAB-737: Stage 3 — live integration on dev.cachekit (interop + stampede + secure cache) - #8
Conversation
…de + secure)
Wire all three components to the live dev.cachekit instance and bind the
Rust-WASM hot path into the TS serving path.
- ingester: build the live backend via the SDK's env-config path. Passing
api_key alone raises ('Both api_url and api_key required'), so live mode
could never start before this; env config also carries the dev-instance
URL + custom-host override. Regression test added.
- edge: deployable on @cachekit-io/cachekit 0.1.3 without the blocked 0.1.4
bump (LAB-780): nodejs_compat flag + wrangler [alias] stubbing the
NAPI-only cachekit-core-ts (statically imported, never executed on the
interop read path; stub throws if that changes). HOTPATH service binding:
every served payload is integrity-checked on the hot path (x-hotpath /
x-hotpath-xxh3 headers; invalid -> 500; unreachable -> honest degrade).
- hotpath: direct worker::Fetch GET replaces WorkersCachekitIO, which
panics on every wasm32 request (SystemTime::now in session_headers) —
filed as LAB-1079; swap back when the SDK fix ships. CACHEKIT_API_URL var
for the dev instance.
- stage3/: committed evidence harness — clean-namespace audit + SDK-free
raw/ciphertext reader (AC-2/4/6) and the async stampede proof (AC-5;
sync wrappers do no distributed locking, documented in LAB-1080).
- docs: architecture spec reconciled (dev.cachekit creds via op://, Render
hosting, cachekit-rs 0.5.0 from crates.io), component READMEs updated.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 3 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (10)
📝 WalkthroughWalkthroughStage 3 adds Rust-WASM hot-path verification for edge cache hits, direct WASM cache reads, environment-gated ingester publishing, and evidence scripts for key derivation, ciphertext inspection, and distributed-lock behaviour. Deployment, architecture, status, and credential documentation are updated accordingly. ChangesStage 3 live integration
Sequence Diagram(s)sequenceDiagram
participant Client
participant EdgeWorker
participant HotpathWorker
participant CachekitAPI
Client->>EdgeWorker: Request cached operation
EdgeWorker->>HotpathWorker: Verify HIT payload
HotpathWorker->>CachekitAPI: Read cache bytes
CachekitAPI-->>HotpathWorker: Payload or miss
HotpathWorker-->>EdgeWorker: Verdict and checksum
EdgeWorker-->>Client: Cached response or integrity error
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
This comment has been minimized.
This comment has been minimized.
- ingester: clear RuntimeError when CACHEKIT_API_KEY exists only in .env — the SDK env config reads process env only, so live mode previously died in pydantic with a misleading 'api_key Field required'. Test added. - edge: a missing HOTPATH binding now degrades like an unreachable hot path (x-hotpath: unavailable + log) instead of silently skipping verification; verdict type tightened (xxh3 required on invalid) and the unavailable reason is logged. Test added. - stage3/raw_read.py: never print backend error bodies into recorded evidence (status code only) — same posture as the hotpath worker. - hotpath/wrangler.toml + docs/architecture.md: stop claiming the SDK allowlist opt-out runs — the LAB-1079 raw-fetch workaround bypasses the SDK entirely; annotate the spec's WorkersCachekitIO pins accordingly. - .gitignore: drop duplicate entry.
This comment has been minimized.
This comment has been minimized.
- stage3/derive_keys.py: rename the master-key literal to
PLACEHOLDER_MASTER_KEY and document why any value works — cache-key
derivation never mixes the master key in (live-verified: the placeholder
derives the exact key the real-master-key ingester wrote), so nothing
credential-shaped is being substituted for a real secret.
- edge/handler.ts: hotpath verify log is now structured
(console.log('hotpath_verify', {operation, window, ...verdict})) so
wrangler tail / Workers Logs can filter on fields.
Remaining Kody items rejected on the PR threads with reasons.
This comment has been minimized.
This comment has been minimized.
|
@kody start-review |
Kody re-fired its hardcoded-credential rule on the renamed placeholder — fair point taken to its conclusion: derive the inert 64-hex value at runtime from a public string (sha256 of a labelled constant) so no literal in source is, or could be swapped for, real key material. Key derivation never mixes the master key in, so output is byte-identical (verified: same 17 keys).
This comment has been minimized.
This comment has been minimized.
|
@kody start-review |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
edge/README.md (1)
61-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAmbiguous parenthetical — clarify which "dev instance" you mean.
The aside right after
skyline-edge.raywalker.workers.devreads as if that URL is the[vars]entry, butCACHEKIT_API_URLactually points at the cachekit API's dev instance (api.dev.cachekit.io), a separate service. Worth a small reword so a skim-reader doesn't conflate the two.✏️ Proposed reword
-Dev deployment: **https://skyline-edge.raywalker.workers.dev** (the dev -instance URL is a `[vars]` entry, `CACHEKIT_API_URL`). Production routing and -a custom domain are Stage 4. +Dev deployment: **https://skyline-edge.raywalker.workers.dev**. The upstream +CachekitIO dev instance URL (`api.dev.cachekit.io`) is a `[vars]` entry, +`CACHEKIT_API_URL`. Production routing and a custom domain are Stage 4.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@edge/README.md` around lines 61 - 63, Clarify the parenthetical in the Dev deployment section so it explicitly identifies CACHEKIT_API_URL as pointing to the separate CacheKit API dev instance at api.dev.cachekit.io, not the skyline-edge worker URL. Keep the existing deployment and staging information unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@README.md`:
- Around line 62-75: Update the “Python ingester” row in the Cost table to
describe it as one free web service that is normally kept warm by a Cloudflare
Worker cron, rather than an always-on worker. Keep the existing free-tier
limits, usage details, and cost unchanged, and leave the footnote consistent
with this wording.
In `@stage3/stampede.py`:
- Around line 72-76: Wrap the probe execution and elapsed-time calculation in a
try/finally structure so the cleanup call to backend.delete(key) always runs,
including when asyncio.gather in the stampede probe flow raises. Keep the
existing timing and result behavior unchanged while moving the deletion into the
finally block.
---
Nitpick comments:
In `@edge/README.md`:
- Around line 61-63: Clarify the parenthetical in the Dev deployment section so
it explicitly identifies CACHEKIT_API_URL as pointing to the separate CacheKit
API dev instance at api.dev.cachekit.io, not the skyline-edge worker URL. Keep
the existing deployment and staging information unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 71757a29-6f7c-4078-8e7b-18a842ae6a7f
⛔ Files ignored due to path filters (1)
hotpath/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (20)
.gitignoreREADME.mddocs/architecture.mdedge/README.mdedge/src/core-ts-stub.tsedge/src/handler.tsedge/src/worker.tsedge/test/handler.test.tsedge/wrangler.tomlhotpath/Cargo.tomlhotpath/README.mdhotpath/src/lib.rshotpath/wrangler.tomlingester/README.mdingester/src/skyline_ingester/__main__.pyingester/tests/test_secure.pystage3/README.mdstage3/derive_keys.pystage3/raw_read.pystage3/stampede.py
- README cost table: describe the Render service honestly — one free web service normally kept warm by the CF cron, not 'always-on' (the free tier spins down; the footnote already said so). - stage3/stampede.py: probe-key delete moved to a finally block so a failed gather can't leave evidence data in dev.cachekit.
This comment has been minimized.
This comment has been minimized.
|
@coderabbitai review |
✅ Action performedReview finished.
|
…nd 3) - elapsed is captured inside the try, right after gather returns — the cleanup delete's round-trip no longer inflates the AC-5 timing evidence. - the finally cleanup is wrapped in its own try/except (log + swallow) so a failing delete can't mask the probe's original exception.
This comment has been minimized.
This comment has been minimized.
|
@kody start-review |
…e pass) Panel v2 verdict SHIP: bug-hunter and security both no-findings; craftsman and catchphrase caught the drift four reactive review rounds left behind. - hotpath/Cargo.toml: drop never-used cachekit-rs features encryption+macros from the wasm32 dep; annotate workers+cachekitio as staged for the LAB-1079 swap-back (nothing uses them today). - hotpath 503 message: point at docs/architecture.md#credentials instead of the now-past 'activate in Stage 3'. - edge handleApi docblock: state the Stage-3 error contract additions (integrity_check_failed 500, x-hotpath: unavailable degrade). - docs/architecture.md#credentials: document the two op:// env-file templates the runbooks reference (references only, no secret material). - stage3: raw_read docstring run-line matches the README; README scopes the venv-reuse claim to the two scripts it's true for; derive_keys placeholder collapsed to a module constant (same bytes out). - ingester tests: hoist the triplicated import block to module top.
dcb6da0
Code Review Completed! 🔥The code review was successfully completed based on your current configurations. Kody Guide: Usage and ConfigurationInteracting with Kody
Current Kody ConfigurationReview OptionsThe following review options are enabled or disabled:
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
Closes LAB-737 (Stage 3 of the Skyline epic LAB-732).
All three Stage-2 components now read and write one live
dev.cachekitnamespace, and the epic's three hardest claims are observed behaviour with recorded evidence (full transcripts on LAB-737):Live proofs (2026-07-29, against
api.dev.cachekit.io)posts_per_minute("5m")aggregate written by the Python ingester was read by the TS edge (200,X-Cache: HIT) and the Rust-WASM hot path (GET /v1/cache/posts_per_minute/5m→found:true,valid_interop_value:true) — same byte-locked keybluesky-thinking:posts_per_minute:230037de…, same decoded value. Byte identity asserted: an SDK-free raw reader and the hot path computed the same xxHash3-64 over the stored bytes (45b601667f405a35on the same generation).POST /v1/cache/{key}/lockacquire +DELETErelease) is in the httpx log (107 acquires incl. waiter polls, 9 releases). Harness committed asstage3/stampede.py. Finding: the lock path is async-only — the sync wrapper does no distributed locking (12/12 recomputes, zero lock calls). Filed LAB-1080 against the cachekit-py docs.language_sentiment("1h")written via@cache.securewith the explicitCachekitIOBackend; an independent raw reader fetched the stored value: 506 bytes, not a MessagePack document, CK envelope + ciphertext, no plaintext markers (avg/langs/sentimentabsent). Hexdump prefix recorded on the ticket.Changes
CachekitIOBackend()via the SDK's env-config path. The oldCachekitIOBackend(api_key=…)call raises (Both api_url and api_key required if using manual config), so live mode had never actually started; env config also carriesCACHEKIT_API_URL/CACHEKIT_ALLOW_CUSTOM_HOSTfor the dev instance (outside the SDK's SSRF allowlist). Regression test added; fail-closed master-key guard asserted live (refuses to start, as designed).skyline-edge.raywalker.workers.dev) on the pinned 0.1.3 without touching the LAB-780-blocked 0.1.4 bump:nodejs_compatflag for transitive node builtins, plus a wrangler[alias]stub for the NAPI-native@cachekit-io/cachekit-core-ts(statically imported, never executed on the interop read path; the stub throws if that ever changes). NewHOTPATHservice binding: every served payload is integrity-checked on the Rust-WASM hot path first —x-hotpath: verified+x-hotpath-xxh3headers on success,500 integrity_check_failedon a corrupt entry, honestx-hotpath: unavailabledegrade if the binding fails. Miss contract untouched (404 +X-Cache: MISS, no hot-path call). 4 new tests.GET /v1/cache/…now works live (503 cleared). The fetch is a directworker::FetchGET:WorkersCachekitIOpanics on every wasm32 request (SystemTime::now()insession_headers(), unimplemented on wasm32-unknown-unknown; all published versions affected) — filed as LAB-1079 with a swap-back note at the call site. Key derivation, strict interop decode and the checksum stay on cachekit-rs / cachekit-core.derive_keys.py(all 17 keys from the real decorator machinery),raw_read.py(SDK-free reader: clean-namespace audit, byte/checksum evidence, ciphertext assertions, cleanup),stampede.py.dev.cachekit+op://cachekit/ck-dev-bluesky-defaultcredentials section replaces the obsoleteckprovisioning runbook; Render replaces Oracle (incl. the AC-8 cost-table row); cachekit-rs git-tag workaround dropped for crates.io 0.5.0; component READMEs updated to match what actually ships.Gates
wrangler deploybundles. hotpath: 13 passed, clippy-D warningson native + wasm32.mainincl. PR LAB-744: fix Review Panel findings (data race, ZK checkpoint leak, restore boot-loop, publish ordering) #5.@cache.secureand shipped interop keys; no encryption/AAD/key-derivation/wire-format code was touched (the hotpath change swaps HTTP transport only).