feat(cloud-facts): signed, versioned, cached facts channel (slice 1, flag off) - #5752
Draft
Hmbown wants to merge 8 commits into
Draft
feat(cloud-facts): signed, versioned, cached facts channel (slice 1, flag off)#5752Hmbown wants to merge 8 commits into
Hmbown wants to merge 8 commits into
Conversation
…st fixtures Add web/scripts/facts-publish.mjs (zero-dependency Node: keygen, sign, verify, emit-sql, publish, revoke; private key and service key only from env at run time, refuses under CI) and docs/cloud-facts/stable.json as the human-edited source of truth for the facts/v1 stable channel. Signing contract: Ed25519 over "codewhale-facts/v1\0" || key_id || "\0" || canonical-JSON payload. docs/cloud-facts/fixtures/ carries a deliberately public TEST-ONLY keypair and two envelopes signed with it (v7 all-sections, v8 applies_to >=99.0.0) shared by the Rust and web test suites; the test-only key is never pinned. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aDEyM2a4pPZ9qqMDrP5YX Signed-off-by: CodeWhale Bot <bot@codewhale.net>
… layer 15 codewhale_config::cloud_facts is network-free: payload types (schema_version 1), pinned Ed25519 trust anchors (TRUSTED_KEYS; the dogfood key cwf-dogfood-2026-08 generated 2026-08-30, private half held outside every repo), verify_envelope (size cap -> shape -> pinned key -> ring Ed25519 -> parse -> cross-checks -> channel -> schema -> applies_to semver scope -> rollback -> not_after grace), scoped_view (per-item applies_to, announcement windows, https + official-host allowlist for base_url), a process-wide overlay with /status provenance, and field-level catalog patch semantics. CatalogSource::CloudFacts / CatalogCompiler::with_cloud_facts sit at layer 15 (above bundled + live models.dev, below provider /v1/models, config, user). PricingProvenance gains CloudFacts. Cloud default_model is consulted only after CLI/env/config declined, so an explicit choice is never overridden. cargo test -p codewhale-config: 642 passed; 0 failed; 1 ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aDEyM2a4pPZ9qqMDrP5YX Signed-off-by: CodeWhale Bot <bot@codewhale.net>
New crate codewhale-cloud-facts modeled on the TUI models_dev_live producer: maybe_load_persisted_cache (bounded sync read, re-verified on every load, tampered cache deleted), refresh (If-None-Match/304, 404 = no facts, body cap, persisted exponential backoff, rollback-protected highest_seen_version), and spawn_background_refresh. Off by default; CODEWHALE_CLOUD_FACTS=1|0, CODEWHALE_DISABLE_CLOUD_FACTS=1 (hard kill), CODEWHALE_CLOUD_FACTS_URL / _CHANNEL / _PATH; CI markers suppress the fetch. Fixed identifier-free User-Agent only. Inert with no active pinned key. examples/fetch_live.rs is the dogfood/proof harness. No new registry entries in Cargo.lock (ring, base64, semver already in the graph). cargo test -p codewhale-cloud-facts: 8 passed; 0 failed (mock HTTP server: 200/304/404/500/oversize, cache round trip, tamper, local path, backoff). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aDEyM2a4pPZ9qqMDrP5YX Signed-off-by: CodeWhale Bot <bot@codewhale.net>
…(default off) - [cloud_facts] config table (enabled=false, channel, url, ttl_hours) next to [update]; documented in config.example.toml. - Startup seam right after the models.dev pair: seed from the re-verified disk cache, then background refresh; nothing blocks startup. - provider_lake merges cloud patches between models.dev live and per-provider live rows (layer 15) and invalidates its memoized merge on overlay updates. - /status gains 'Catalog:' (models.dev freshness/count/age) and 'Cloud facts:' (channel/version/key/age/origin or why bundled is in use) rows; MessageIds added to all 15 locale bundles. - /model refresh also forces the cloud facts fetch when enabled. - Picker and fleet badges label CatalogSource::CloudFacts rows. CARGO_BUILD_JOBS=4 cargo check -p codewhale-tui: Finished (0 warnings). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aDEyM2a4pPZ9qqMDrP5YX Signed-off-by: CodeWhale Bot <bot@codewhale.net>
…nnel> web/lib/cloud-facts.ts reads public.facts_current from the CodeWhale Web Supabase project over PostgREST with the publishable (anon) key only, rebuilds the envelope, recomputes the payload SHA-256 (mismatch -> 502), and verifies the Ed25519 signature against web/lib/cloud-facts/keys.ts (failure -> 503; the edge never amplifies a bad row; an empty key table skips verification and reports it). keys.ts mirrors crates/config/src/cloud_facts/keys.rs; check:facts fails on divergence. The route answers GET/HEAD with a strong ETag, If-None-Match -> 304, CDN cache headers (s-maxage=300, stale-while-revalidate, stale-if-error), no Set-Cookie/Vary/query params, and a KV last-good copy (CURATED_KV) served as X-Facts-Source: kv-stale when Supabase errors or times out (3 s budget); both down -> 503 Retry-After: 600. /api/facts gains an additive cloudFacts summary block; schemaVersion unchanged. Env names SUPABASE_URL / SUPABASE_PUBLISHABLE_KEY added to .env.example and wrangler.jsonc vars (values set by the founder); .dev.vars ignored. Verification skip now requires a truly empty key table, so a retired-only table still rejects instead of silently skipping. npm test: 355 passed, 0 failed (41 files). npm run check:facts OK (check-cloud-facts: keys.rs == keys.ts, stable.json release.latest == latest-published-release.json, fixtures verify). tsc --noEmit and eslint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aDEyM2a4pPZ9qqMDrP5YX Signed-off-by: CodeWhale Bot <bot@codewhale.net>
… vocabulary docs/CLOUD_FACTS.md documents the facts/v1 channel end to end: envelope and domain-separated signing (Ed25519 over DOMAIN || key_id || payload), the Supabase schema with forced RLS and immutability/monotonic triggers, key custody and rotation runbook (the dogfood key cwf-dogfood-2026-08 was generated 2026-08-30; its private half lives outside every repo, noted for founder custody), the publish/revoke runbook, the /status vocabulary, the two-release key-then-flip rule, and the codewhale.net hosting discrepancy (Cloudflare Worker via OpenNext, not Vercel as the PRD states; the route is host-agnostic). CATALOG_REFRESH.md gains the layer-15 paragraph; CHANGELOG records the feature behind [cloud_facts].enabled (default off). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aDEyM2a4pPZ9qqMDrP5YX Signed-off-by: CodeWhale Bot <bot@codewhale.net>
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 36729265 | Triggered | Generic Private Key | 9e83f81 | docs/cloud-facts/fixtures/test-only-signing-key.pem | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secret safely. Learn here the best practices.
- Revoke and rotate this secret.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
…re key docs/cloud-facts/fixtures/test-only-signing-key.pem is a deliberately public Ed25519 test fixture (never pinned, signs nothing outside tests). Silences the false-positive 'Generic Private Key' incident from 9e83f81. Signed-off-by: CodeWhale Bot <bot@codewhale.net>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Slice 1 of the Supabase-backed cloud facts channel: signed + versioned + cached + fail-safe facts (model catalog deltas, provider defaults, release truth, announcements) served by the website at /api/facts/v1/, consumed by a client behind
[cloud_facts].enabled(default OFF), provenance in /status, schema ready for per-org override.Evidence
cargo test -p codewhale-cloud-facts→ 8 passed;cargo test -p codewhale-config cloud_facts→ 18 passed; webnpm test→ 355 passed;check:factsOK; tsc + lint clean.Unproven / before merge
No-Issue: cloud facts lane per founder dogfood-Supabase direction