Skip to content

LAB-737: Stage 3 — live integration on dev.cachekit (interop + stampede + secure cache) - #8

Merged
27Bslash6 merged 7 commits into
mainfrom
lab-737-stage3-live-integration
Jul 29, 2026
Merged

LAB-737: Stage 3 — live integration on dev.cachekit (interop + stampede + secure cache)#8
27Bslash6 merged 7 commits into
mainfrom
lab-737-stage3-live-integration

Conversation

@27Bslash6

Copy link
Copy Markdown
Contributor

Closes LAB-737 (Stage 3 of the Skyline epic LAB-732).

All three Stage-2 components now read and write one live dev.cachekit namespace, 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)

  • AC-4 / epic AC-7 — cross-SDK interop: a 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/5mfound:true, valid_interop_value:true) — same byte-locked key bluesky-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 (45b601667f405a35 on the same generation).
  • AC-5 / epic AC-5 — stampede: 12 concurrent async callers on one cold key → exactly 1 recompute, all callers served the winner's value; the SaaS lock traffic (POST /v1/cache/{key}/lock acquire + DELETE release) is in the httpx log (107 acquires incl. waiter polls, 9 releases). Harness committed as stage3/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.
  • AC-6 / epic AC-6 — zero-knowledge: language_sentiment("1h") written via @cache.secure with the explicit CachekitIOBackend; an independent raw reader fetched the stored value: 506 bytes, not a MessagePack document, CK envelope + ciphertext, no plaintext markers (avg/langs/sentiment absent). Hexdump prefix recorded on the ticket.

Changes

  • ingester — live mode now builds CachekitIOBackend() via the SDK's env-config path. The old CachekitIOBackend(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 carries CACHEKIT_API_URL / CACHEKIT_ALLOW_CUSTOM_HOST for the dev instance (outside the SDK's SSRF allowlist). Regression test added; fail-closed master-key guard asserted live (refuses to start, as designed).
  • edge — deployed (skyline-edge.raywalker.workers.dev) on the pinned 0.1.3 without touching the LAB-780-blocked 0.1.4 bump: nodejs_compat flag 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). New HOTPATH service binding: every served payload is integrity-checked on the Rust-WASM hot path first — x-hotpath: verified + x-hotpath-xxh3 headers on success, 500 integrity_check_failed on a corrupt entry, honest x-hotpath: unavailable degrade if the binding fails. Miss contract untouched (404 + X-Cache: MISS, no hot-path call). 4 new tests.
  • hotpathGET /v1/cache/… now works live (503 cleared). The fetch is a direct worker::Fetch GET: WorkersCachekitIO panics on every wasm32 request (SystemTime::now() in session_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.
  • stage3/ — committed evidence harness: 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.
  • docs — architecture spec reconciled (AC-0): dev.cachekit + op://cachekit/ck-dev-bluesky-default credentials section replaces the obsolete ck provisioning 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

  • ingester: 45 passed, ruff clean (incl. stage3). edge: 24 passed, eslint/tsc/prettier clean, wrangler deploy bundles. hotpath: 13 passed, clippy -D warnings on native + wasm32.
  • AC-2: clean-namespace audit ran before the first live run — 17/17 keys absent (both auto-mode keys, the ZK-hazard carriers, never existed; one spike-roundtrip residue deleted first). Running code is main incl. PR LAB-744: fix Review Panel findings (data race, ZK checkpoint leak, restore boot-loop, publish ordering) #5.
  • Crypto/protocol gate: N/A — this consumes shipped @cache.secure and shipped interop keys; no encryption/AAD/key-derivation/wire-format code was touched (the hotpath change swaps HTTP transport only).
  • Expert-panel review: run before in_review; outcome recorded on LAB-737.

…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.
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c118e1cf-8f49-4cb2-97f4-a692725ecdef

📥 Commits

Reviewing files that changed from the base of the PR and between 2ca7d6f and dcb6da0.

⛔ Files ignored due to path filters (1)
  • hotpath/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • README.md
  • docs/architecture.md
  • edge/src/handler.ts
  • hotpath/Cargo.toml
  • hotpath/src/lib.rs
  • ingester/tests/test_secure.py
  • stage3/README.md
  • stage3/derive_keys.py
  • stage3/raw_read.py
  • stage3/stampede.py
📝 Walkthrough

Walkthrough

Stage 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.

Changes

Stage 3 live integration

Layer / File(s) Summary
WASM hot-path cache reads
hotpath/*
The hotpath Worker fetches authenticated cache payloads directly and maps response statuses for verification.
Edge integrity verification
edge/src/*, edge/test/*, edge/wrangler.toml
Cache HIT payloads are verified through the optional hotpath binding, with integrity errors, status headers, service wiring, and tests for verified, invalid, unavailable, and MISS paths.
Environment-gated ingester publishing
ingester/*, .gitignore
Live publishing requires CACHEKIT_API_KEY in the process environment, while dry-run behaviour remains available and the environment path is tested.
Stage 3 evidence probes
stage3/*
Executable probes cover derived keys, raw cache and ciphertext checks, and asynchronous stampede-lock validation.
Architecture and deployment documentation
README.md, docs/architecture.md
Stage 3 status, endpoints, runtime pins, credentials, hosting, deployment, and evidence harness details are updated.

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
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the PR’s main change: Stage 3 live integration on dev.cachekit across interop, stampede, and secure cache.
Description check ✅ Passed The description is directly about the Stage 3 live integration and evidence work shown in the diff.
Docstring Coverage ✅ Passed Docstring coverage is 88.24% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@kodus-27b

This comment has been minimized.

Comment thread ingester/README.md
Comment thread ingester/src/skyline_ingester/__main__.py
Comment thread stage3/derive_keys.py
Comment thread stage3/derive_keys.py Outdated
Comment thread stage3/derive_keys.py

@kodus-27b kodus-27b Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- 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.
@kodus-27b

This comment has been minimized.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 29, 2026
Comment thread edge/src/handler.ts
Comment thread edge/src/handler.ts Outdated
Comment thread ingester/tests/test_secure.py
Comment thread stage3/raw_read.py
- 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.
@kodus-27b

This comment has been minimized.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 29, 2026
@27Bslash6

Copy link
Copy Markdown
Contributor Author

@kody start-review

Comment thread stage3/derive_keys.py Outdated
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).
@kodus-27b

This comment has been minimized.

@27Bslash6

Copy link
Copy Markdown
Contributor Author

@kody start-review

kodus-27b[bot]
kodus-27b Bot previously approved these changes Jul 29, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
edge/README.md (1)

61-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Ambiguous parenthetical — clarify which "dev instance" you mean.

The aside right after skyline-edge.raywalker.workers.dev reads as if that URL is the [vars] entry, but CACHEKIT_API_URL actually 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

📥 Commits

Reviewing files that changed from the base of the PR and between 2b351e9 and 2ca7d6f.

⛔ Files ignored due to path filters (1)
  • hotpath/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (20)
  • .gitignore
  • README.md
  • docs/architecture.md
  • edge/README.md
  • edge/src/core-ts-stub.ts
  • edge/src/handler.ts
  • edge/src/worker.ts
  • edge/test/handler.test.ts
  • edge/wrangler.toml
  • hotpath/Cargo.toml
  • hotpath/README.md
  • hotpath/src/lib.rs
  • hotpath/wrangler.toml
  • ingester/README.md
  • ingester/src/skyline_ingester/__main__.py
  • ingester/tests/test_secure.py
  • stage3/README.md
  • stage3/derive_keys.py
  • stage3/raw_read.py
  • stage3/stampede.py

Comment thread README.md
Comment thread stage3/stampede.py Outdated
- 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.
@kodus-27b

This comment has been minimized.

@27Bslash6

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 29, 2026
Comment thread stage3/stampede.py Outdated
Comment thread stage3/stampede.py Outdated
…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.
@kodus-27b

This comment has been minimized.

@27Bslash6

Copy link
Copy Markdown
Contributor Author

@kody start-review

Comment thread stage3/stampede.py
Comment thread stage3/stampede.py
kodus-27b[bot]
kodus-27b Bot previously approved these changes Jul 29, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 29, 2026
…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.
@kodus-27b

kodus-27b Bot commented Jul 29, 2026

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

Comment thread ingester/tests/test_secure.py
@27Bslash6

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@27Bslash6

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@27Bslash6
27Bslash6 merged commit 0a75b07 into main Jul 29, 2026
3 checks passed
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