From 6b8cef3dd666850f54bc7e3898a4b2a56719c658 Mon Sep 17 00:00:00 2001 From: Nicklas Lundin Date: Tue, 1 Sep 2026 14:19:20 +0200 Subject: [PATCH] docs: refresh agent memories Co-Authored-By: Codex --- .github/workflows/CLAUDE.md | 4 ++-- CLAUDE.md | 29 ++++++++++++++++-------- confidence-cloudflare-resolver/CLAUDE.md | 8 +++++++ openfeature-provider/go/CLAUDE.md | 16 +++++++------ openfeature-provider/java/CLAUDE.md | 12 +++++++--- openfeature-provider/js/CLAUDE.md | 16 +++++++++---- openfeature-provider/python/CLAUDE.md | 22 +++++++++++------- openfeature-provider/rust/CLAUDE.md | 5 +++- wasm-msg/CLAUDE.md | 4 ++-- wasm/rust-guest/CLAUDE.md | 8 +++---- 10 files changed, 84 insertions(+), 40 deletions(-) diff --git a/.github/workflows/CLAUDE.md b/.github/workflows/CLAUDE.md index d8b38c3dd..75815779b 100644 --- a/.github/workflows/CLAUDE.md +++ b/.github/workflows/CLAUDE.md @@ -9,7 +9,7 @@ CI is a **single `docker build .`** — the multi-stage Dockerfile is the entire ## Release & Publish (`release-please.yml`) -Release Please detects version bumps per component independently. Each component has its own conditional publish job. +Release Please detects version bumps per component independently. Published packages have conditional publish jobs; the Go provider is released by tag only, and internal WASM crates are versioned without separate publish jobs. ### Publish dependency chain @@ -20,7 +20,7 @@ The Rust provider (`openfeature-provider/rust`) depends on `confidence-resolver` 3. If only the Rust provider is released (resolver not changed), it skips the wait -All publish jobs require the **`deployment` GitHub environment** (environment protection rules apply). +Package publish jobs require the **`deployment` GitHub environment** (environment protection rules apply). The Cloudflare deployer image jobs publish directly to GHCR and do not use that environment. ### Post-release diff --git a/CLAUDE.md b/CLAUDE.md index 91dd73c04..7fffbfd6b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,14 +2,16 @@ ## Repository Overview -Multi-language workspace implementing feature flag resolution in Rust, compiled to WebAssembly with bindings for JS, Java, Go, Ruby, Rust, and Python. +Multi-language workspace implementing feature flag resolution and event tracking in Rust. The resolver and event engine compile to WebAssembly for JS, Java, Go, and Python; the Rust provider uses the resolver natively, while Ruby resolves remotely over HTTP. **Repository**: `spotify/confidence-resolver` ### Key Components - **confidence-resolver/** — Core Rust resolver library (flag evaluation, targeting, bucketing) +- **confidence-event-engine/** — Shared event batching engine - **wasm/rust-guest/** — WASM guest (compiles the resolver to `wasm32-unknown-unknown`) +- **wasm/event-guest/** — WASM guest for OpenFeature event tracking - **wasm-msg/** — WASM messaging layer (alloc/free, protobuf-based host↔guest calls) - **confidence-cloudflare-resolver/** — Cloudflare Worker WASM build - **openfeature-provider/js/** — TypeScript OpenFeature provider (npm: `@spotify-confidence/openfeature-server-provider-local`) @@ -18,7 +20,7 @@ Multi-language workspace implementing feature flag resolution in Rust, compiled - **openfeature-provider/ruby/** — Ruby OpenFeature provider (**online/remote resolver, NOT WASM**) - **openfeature-provider/rust/** — Rust OpenFeature provider (**native resolver, no WASM**) - **openfeature-provider/python/** — Python OpenFeature provider -- **openfeature-provider/proto/** — Shared protobuf definitions used by all providers +- **openfeature-provider/proto/** — Shared protobuf definitions used by the local providers - **mock-support-server/** — Go mock server for integration/benchmark testing ## Cargo Workspace Gotcha @@ -28,13 +30,15 @@ Several workspace members are **dummy Cargo crates** that exist solely for Relea ## WASM Architecture ``` -Host (JS/Java/Go/Python/Ruby) +Host (JS/Java/Go/Python) ↓ protobuf message via wasm-msg (alloc → write → call → read → free) WASM Guest (rust-guest, compiled from confidence-resolver) ↓ returns protobuf response Host ``` +Event tracking in those providers follows the same host/guest pattern through `wasm/event-guest`, backed by `confidence-event-engine`. Ruby does not use either WASM guest, and the Rust provider links the resolver crate directly. + - `wasm-msg` provides memory management (`wasm_msg_alloc`/`wasm_msg_free`) and the `wasm_msg_guest!`/`wasm_msg_host!` macros - Guest exports are prefixed: `wasm_msg_guest_resolve_flags`, `wasm_msg_guest_set_resolver_state`, etc. - Host imports are prefixed: `wasm_msg_host_log_message`, `wasm_msg_host_current_time` @@ -70,13 +74,19 @@ make sync-wasm-go This builds the WASM in Docker and copies it to `openfeature-provider/go/confidence/internal/local_resolver/assets/`. The updated `.wasm` file must be committed. +The Go provider also embeds the event engine WASM. After changes to `confidence-event-engine/` or `wasm/event-guest/`, run: + +```bash +make sync-wasm-event-go +``` + ## Protobuf Schema Locations There are 4 separate proto directories — this is the most common source of confusion: - **`confidence-resolver/protos/`** — Core resolver protos (flags, admin, resolver API, types, events) -- **`openfeature-provider/proto/`** — Shared provider protos (WASM messages, flag types) — used by JS, Java, Go, Ruby, Python providers -- **`wasm/proto/`** — WASM guest message definitions (`messages.proto`, `types.proto`) +- **`openfeature-provider/proto/`** — Shared provider protos (flags, WASM messages, and events) — used by JS, Java, Go, Python, and the published Rust provider +- **`wasm/proto/`** — Resolver WASM guest message definitions (`messages.proto`, `types.proto`, and resolver API imports) - **`wasm-msg/proto/`** — Low-level messaging layer protos ## Publishing & Security @@ -94,7 +104,7 @@ RUN --mount=type=secret,id=my_secret \ ``` - **JS** — Build in Docker (`npm pack`), publish via GitHub Actions OIDC (no npm tokens). Requires npm Trusted Publishers config. -- **Java** — Credentials mounted as Docker secrets. Requires GitHub secrets: `MAVEN_SETTINGS`, `GPG_PRIVATE_KEY`, `SIGN_KEY_PASS`. Uses `central-publishing-maven-plugin` (not nexus-staging). +- **Java** — Credentials mounted as Docker secrets. Requires GitHub secrets: `MAVEN_CENTRAL_USERNAME`, `MAVEN_CENTRAL_PASSWORD`, `GPG_PRIVATE_KEY`, `SIGN_KEY_PASS`. Uses `central-publishing-maven-plugin` (not nexus-staging). - **Rust** — Published via Docker stages to crates.io. ## Post-Release Checklist @@ -109,14 +119,15 @@ After releasing a new version of any SDK from this repo, update internal version make # lint + test + build everything make test # run all component tests make lint # run all linters -make build # build WASM + all provider packages +make build # build both WASM artifacts and provider build targets make wasm/confidence_resolver.wasm # build WASM artifact only make sync-wasm-go # build WASM in Docker and sync to Go assets (for committing) +make sync-wasm-event-go # build event WASM in Docker and sync to Go assets make go-bench # Go benchmark via docker-compose make js-bench # JS benchmark via docker-compose ``` -Each component has its own Makefile with `build`, `test`, `lint`, `clean` targets. +Component Makefiles expose the targets relevant to that component; not every component implements every target. ### Docker @@ -130,7 +141,7 @@ docker build --target openfeature-provider-js.test . # run JS tests docker build --target openfeature-provider-java.build . # build Java provider ``` -Stage naming pattern: `.{build,test,test_e2e,lint,artifact,publish}` +Common stage naming pattern: `.{build,test,test_e2e,lint,artifact,publish}`. Available actions vary by component. ## Environment Variables diff --git a/confidence-cloudflare-resolver/CLAUDE.md b/confidence-cloudflare-resolver/CLAUDE.md index 7275edf3d..b3e900357 100644 --- a/confidence-cloudflare-resolver/CLAUDE.md +++ b/confidence-cloudflare-resolver/CLAUDE.md @@ -11,6 +11,7 @@ A Cloudflare Worker that serves the Confidence flag resolver at the edge. Compil - **Compile-time state** — Resolver state is embedded at build time via `include_bytes!("../../data/resolver_state_current.pb")`. No runtime state fetching. Redeployment required to update. - **Optional sticky assignments** — Opt-in via `ENABLE_STICKY_ASSIGNMENTS` (KV-backed). - **Queue-based log shipping** — Flag logs are serialized to JSON and sent to a Cloudflare Queue (`flag_logs_queue`), then consumed by a queue handler that aggregates and ships them. +- **Resolver telemetry** — Resolve latency/rate telemetry is included in flag-log batches; the queue consumer can accumulate Prometheus snapshots in KV. - **JSON API** — Request/response bodies are JSON (not protobuf), unlike the WASM-based providers. - **CORS** — All responses include CORS headers with configurable `ALLOWED_ORIGIN`. @@ -22,8 +23,11 @@ A Cloudflare Worker that serves the Confidence flag resolver at the edge. Compil | POST | `/v1/flags:apply` | Apply flags (JSON body) | | POST | `/v1/events:publish` | Publish events (JSON body, queued and batched before delivery to backend) | | GET | `/v1/state:etag` | Returns deployment state ETag and resolver version | +| GET | `/metrics` | Returns KV-backed Prometheus metrics (requires client-secret authorization) | | OPTIONS | `*` | CORS preflight | +`POST /v1/telemetry:upload` is accepted as a no-op for SDK compatibility. + Note: The Worker router uses `*path` matching because `:name` conflicts with Cloudflare router parameter syntax. ## Queue Consumers @@ -53,6 +57,9 @@ Both queues use `max_batch_size=100` and `max_batch_timeout=10s`. | `MATERIALIZATION_TTL_SECONDS` | TTL for KV-backed sticky assignments (optional) | | `FORCE_APPLY` | Default `true`: force `apply=true` on every resolve. Set `"false"` to honor the SDK-sent `apply` value (deferred apply) | | `RESOLVE_TOKEN_ENCRYPTION_KEY` | (secret) AES-128 key for resolve token encryption; auto-generated by deployer | +| `ENABLE_APPLY_DEDUP` | Experimental: deduplicate repeated assignment events; defaults to `false` | + +The deployer also accepts `ENABLE_METRICS` to create and bind `CONFIDENCE_METRICS_KV`; it is a deployer setting rather than a Worker environment variable. ## Build & Test @@ -91,3 +98,4 @@ The `deployer/` directory contains a deployment script and Dockerfile for automa 3. **JSON, not protobuf** — API uses JSON request/response bodies 4. **Edge deployment** — Runs on Cloudflare's edge network, not in application processes 5. **Queue logging** — Uses Cloudflare Queues instead of gRPC for log transport +6. **Optional Prometheus endpoint** — Queue telemetry is accumulated in KV for `/metrics` diff --git a/openfeature-provider/go/CLAUDE.md b/openfeature-provider/go/CLAUDE.md index a07f8aed3..5854a01e3 100644 --- a/openfeature-provider/go/CLAUDE.md +++ b/openfeature-provider/go/CLAUDE.md @@ -9,16 +9,17 @@ Go OpenFeature provider using the Confidence resolver compiled to WASM, loaded v ## Architecture - **WASM runtime**: wazero — pure Go, zero dependencies, no CGo -- **WASM embedding**: The WASM binary is embedded at compile time via `//go:embed` in `confidence/internal/local_resolver/wasm.go` -- **Pool architecture**: Multiple WASM instances run in a pool (`pool.go`) sized to `GOMAXPROCS` by default. Each instance is mutex-protected. +- **WASM embedding**: Resolver and event-engine WASM binaries are embedded at compile time via `//go:embed` +- **Pool architecture**: Resolver WASM instances run in a pool (`pool.go`) with a default size of 2, capped at `GOMAXPROCS`. Each instance is mutex-protected. - **Crash recovery**: `recover.go` wraps resolvers with automatic WASM instance reload on panic/trap, preserving state and buffering logs through crashes. -- **gRPC**: Flag logs are shipped via gRPC to `edge-grpc.spotify.com` using `InternalFlagLoggerServiceClient`. +- **Destination-aware flag logs**: Resolver state selects gRPC delivery to Spotify Edge or HTTP delivery to the Cloudflare ingestor, with ordered fallback. +- **Event tracking**: OpenFeature `Track` calls are batched by `confidence_event_engine.wasm` and published to the events service over gRPC. ## Key API - **`NewProvider(ctx, ProviderConfig)`** (`provider_builder.go`) — Main factory function. Creates gRPC connection, state fetcher, flag logger, and wires everything together. - **`NewProviderForTest(ctx, ProviderTestConfig)`** — Factory with injectable `StateProvider` and `FlagLogger` for testing. -- **`ProviderConfig`** — `ClientSecret`, `Logger`, `TransportHooks`, `MaterializationStore`, `UseRemoteMaterializationStore`, `StatePollInterval`, `LogPollInterval`, `ResolverPoolSize`, `UseWasmInterpreter` +- **`ProviderConfig`** — `ClientSecret`, `EncryptionKey`, `Logger`, `TransportHooks`, `MaterializationStore`, `UseRemoteMaterializationStore`, `StatePollInterval`, `LogPollInterval`, `ResolverPoolSize`, `UseWasmInterpreter`, `EnableApplyDedup`, `DisableExposureCollection` - **`TransportHooks`** interface — Allows customizing both gRPC and HTTP transports (for proxying or testing): `ModifyGRPCDial(target, opts)` and `WrapHTTP(transport)`. ## Build & Test @@ -34,10 +35,11 @@ make proto The provider starts background goroutines on `Init()`: 1. **State polling** — Fetches resolver state from CDN at `StatePollInterval` (default 10s) -2. **Log flushing** — Flushes resolve + assign logs via gRPC at `LogPollInterval` (default 15s) +2. **Log flushing** — Flushes resolve + assign logs at `LogPollInterval` (default 15s) +3. **Event flushing** — Flushes tracked events at `LogPollInterval` -Both are cancelled via context on `Shutdown()`. +All are cancelled via context on `Shutdown()`; pending events are drained before the event tracker closes. ## WASM Build -WASM is automatically built from source and copied to `confidence/internal/local_resolver/assets/` when building locally (skipped in Docker where it's provided by the build stage). +The resolver WASM is automatically built from source and copied to `confidence/internal/local_resolver/assets/` when building locally (skipped in Docker where it is provided by the build stage). The committed resolver and event-engine binaries are synchronized reproducibly with `make sync-wasm-go` and `make sync-wasm-event-go` from the repository root. diff --git a/openfeature-provider/java/CLAUDE.md b/openfeature-provider/java/CLAUDE.md index af344fe79..647465719 100644 --- a/openfeature-provider/java/CLAUDE.md +++ b/openfeature-provider/java/CLAUDE.md @@ -4,14 +4,20 @@ Maven coordinates: `com.spotify.confidence:openfeature-provider-local` -Java OpenFeature provider using the Confidence resolver compiled to WASM, with Chicory AOT compilation for near-native performance. +Java OpenFeature provider using the Confidence resolver compiled to WASM, with Chicory AOT compilation for near-native flag resolution and a separate event-engine WASM for OpenFeature tracking. ## Key Architecture - **Chicory WASM AOT** — The WASM binary (`src/main/resources/wasm/confidence_resolver.wasm`) is AOT-compiled to Java bytecode at build time via `chicory-compiler-maven-plugin`. This generates `com.spotify.confidence.sdk.ConfidenceResolverModule`. -- **gRPC** — Used for flag log shipping and materialization store communication. Protobuf + gRPC stubs are generated from `../proto/`. +- **Resolver pool and recovery** — A configurable pool (default 2, capped at available processors) wraps recovering resolver instances. +- **Event tracking** — `confidence_event_engine.wasm` is loaded through Chicory at runtime; tracked events are flushed every 15 seconds and published over gRPC. +- **Transport** — Flag logs use destination-aware gRPC/HTTP delivery. gRPC is also used for event publishing and remote materializations. Protobuf + gRPC stubs are generated from `../proto/`. - **Shaded JAR** — gRPC, protobuf, and guava are relocated to `com.spotify.confidence.sdk.shaded.*` to avoid version conflicts with consumers. +## Configuration + +`LocalProviderConfig.builder()` supports custom channel and HTTP client factories, remote materializations, resolver pool size, an optional AES-256 state encryption key, experimental apply deduplication, and disabling exposure collection. + ## Main Provider Class ```java @@ -28,7 +34,7 @@ public class OpenFeatureLocalResolveProvider implements FeatureProvider { ## Build & Test ```bash -make build # build WASM (if needed) + mvn package -DskipTests +make build # build both WASM resources (if needed) + mvn package -DskipTests make test # build + mvn test (excludes *E2ETest) make test-e2e # build + mvn verify (integration tests against shaded JAR) ``` diff --git a/openfeature-provider/js/CLAUDE.md b/openfeature-provider/js/CLAUDE.md index 59e2ab897..d4bf0d665 100644 --- a/openfeature-provider/js/CLAUDE.md +++ b/openfeature-provider/js/CLAUDE.md @@ -4,11 +4,11 @@ npm package: `@spotify-confidence/openfeature-server-provider-local` -TypeScript OpenFeature provider using the Confidence resolver compiled to WASM. Supports multiple WASM loading strategies and React Server Components. +TypeScript OpenFeature provider using resolver and event-engine WASM modules. Supports multiple WASM loading strategies, React Server Components, and Next.js Pages Router helpers. ## Entry Points & Exports -The package has 5 build targets configured in `tsdown.config.ts`: +The package has 8 build targets configured in `tsdown.config.ts`: | Export Path | Entry File | Platform | WASM Loading | | ------------------ | ---------------------- | -------- | ------------------------------- | @@ -17,10 +17,13 @@ The package has 5 build targets configured in `tsdown.config.ts`: | `"./fetch"` | `src/index.fetch.ts` | neutral | `fetch()` (Deno, Bun, browsers) | | `"./react-server"` | `src/react/server.tsx` | neutral | React Server Component | | `"./react-client"` | `src/react/client.tsx` | neutral | React Client Component | +| `"./pages-router/server"` | `src/pages-router/server.ts` | neutral | Pages Router server helpers | +| `"./pages-router/client"` | `src/pages-router/client.tsx` | neutral | Pages Router client provider | +| `"./pages-router/api"` | `src/pages-router/api.ts` | neutral | Deferred-apply API handler | -Each entry point exports a `createConfidenceServerProvider` factory function. +The default, `./node`, and `./fetch` entry points export `createConfidenceServerProvider`; the React and Pages Router entry points expose framework-specific APIs. -The `./node` entry point extends options with `wasmPath?: string` and `./fetch` with `wasmUrl?: URL | string`. +The default entry point inlines both WASM modules. The `./node` and `./fetch` builds copy both modules alongside the bundle; their `wasmPath`/`wasmUrl` overrides apply to the resolver module. ## ProviderOptions @@ -29,14 +32,19 @@ Defined in `src/ConfidenceServerProviderLocal.ts`: ```typescript interface ProviderOptions { flagClientSecret: string; + encryptionKey?: string; // hex-encoded AES-256 key for CDN state initializeTimeout?: number; stateUpdateInterval?: number; // ms between state polls (default: 30000) flushInterval?: number; // ms between log flushes (default: 15000) fetch?: typeof fetch; materializationStore?: MaterializationStore | 'CONFIDENCE_REMOTE_STORE'; + enableApplyDedup?: boolean; // experimental, default false + disableExposureCollection?: boolean; } ``` +OpenFeature `track()` calls are buffered in `confidence_event_engine.wasm` and published over HTTP on the regular flush interval. + ## Build & Test ```bash diff --git a/openfeature-provider/python/CLAUDE.md b/openfeature-provider/python/CLAUDE.md index 212ce519f..bae0f100d 100644 --- a/openfeature-provider/python/CLAUDE.md +++ b/openfeature-provider/python/CLAUDE.md @@ -4,28 +4,34 @@ PyPI package: `confidence-openfeature-provider` -Python OpenFeature provider using the Confidence resolver compiled to WASM, loaded via wasmtime. +Python OpenFeature provider using Confidence resolver and event-engine WASM modules, loaded via wasmtime. ## Architecture - **wasmtime** — WASM runtime - **Crash recovery** — `LocalResolver` wraps `WasmResolver` with automatic WASM instance reload on `RuntimeError` or `wasmtime.Trap`, caching state for recovery and buffering logs through crashes -- **WASM loading** — Binary loaded from package resources via `importlib.resources` (Python 3.9+) with `pkg_resources` fallback +- **WASM loading** — Both binaries are loaded from package resources via `importlib.resources`, with compatibility fallbacks - **Threading** — Background threads for state polling and log flushing (not asyncio) -- **gRPC** — Flag logs shipped via `grpcio` +- **Destination-aware flag logs** — Resolver state selects gRPC or Cloudflare HTTP delivery with fallback +- **Event tracking** — OpenFeature `track()` calls are batched in `confidence_event_engine.wasm` and published over gRPC - **httpx** — HTTP client for state fetching from CDN ## Background Threads -The provider starts background threads: +The provider starts two long-lived background threads: 1. **State polling** — Fetches resolver state from CDN (default 30s) -2. **Log flushing** — Flushes resolve + assign logs via gRPC (default 15s) -3. **Assign flushing** — Flushes assign logs at higher frequency (default 100ms) +2. **Flush loop** — Flushes resolve logs and tracked events every 15s, and assign logs every 100ms + +Event publishing uses a small thread-pool executor so gRPC publishing does not block the flush loop. + +## Provider Options + +In addition to polling and materialization settings, `ConfidenceProvider` accepts an optional AES-256 state `encryption_key`, experimental `enable_apply_dedup`, and `disable_exposure_collection`. ## Build & Test ```bash -make build # build WASM + create venv + install + python -m build +make build # build both WASM resources + create venv + install + python -m build make test # pytest tests/ (excludes e2e) make test-e2e # pytest e2e tests make lint # ruff check + ruff format --check + mypy @@ -36,5 +42,5 @@ make install # create venv + pip install -e ".[dev]" ## Gotchas -- **WASM packaging**: The `hatchling` build system includes the WASM binary in the wheel via `force-include` from `resources/wasm/`. Locally, WASM is built from source and copied there. In Docker, it's provided by the build stage. +- **WASM packaging**: The `hatchling` build system includes both WASM binaries in the wheel via `force-include` from `resources/wasm/`. Locally, they are built from source and copied there. In Docker, they are provided by build stages. - **Proto location**: Generated from `../proto/` (i.e., `openfeature-provider/proto/`), output goes to `src/confidence/proto/`. diff --git a/openfeature-provider/rust/CLAUDE.md b/openfeature-provider/rust/CLAUDE.md index bb2fde48c..d0babd9ef 100644 --- a/openfeature-provider/rust/CLAUDE.md +++ b/openfeature-provider/rust/CLAUDE.md @@ -11,8 +11,11 @@ Rust OpenFeature provider for Confidence. **Uses the `confidence_resolver` crate - **Native resolver** — Links directly to `confidence_resolver` crate - **Async** — Built on `tokio` with background tasks for state polling and log flushing - **`reqwest`** — HTTP client for state fetching from CDN and log shipping +- **Destination-aware logging** — Resolver state selects Spotify Edge or Cloudflare HTTP delivery with ordered fallback - **Builder pattern** — `ProviderOptions::new(secret).with_*()` chain for configuration - **`gateway_url`** option — Routes all HTTP requests through a proxy, preserving original host in `X-Forwarded-Host` +- **Encrypted state** — Optional hex-encoded AES-256 key decrypts CDN state +- **Exposure control** — `with_disable_exposure_collection()` disables assignment collection while retaining resolve logs and telemetry ## Background Tasks @@ -34,4 +37,4 @@ make lint # cargo fmt --check + cargo clippy -- -D warnings ## Proto Generation -`build.rs` compiles protos from `../../confidence-resolver/protos/` — specifically `internal_api.proto` for the remote materialization API. Generated code is included as `remote_proto` module. +`build.rs` compiles `types.proto` and `internal_api.proto` for the remote materialization API. It uses the crate-local `proto/` directory in a published crate and falls back to the shared `openfeature-provider/proto/` directory during workspace development. Generated code is included as the `remote_proto` module. diff --git a/wasm-msg/CLAUDE.md b/wasm-msg/CLAUDE.md index a6f73081d..890e7b379 100644 --- a/wasm-msg/CLAUDE.md +++ b/wasm-msg/CLAUDE.md @@ -20,7 +20,7 @@ All allocations store the total allocation size (as `usize`) immediately before ## Message Protocol -Every guest↔host call goes through protobuf envelope types (defined in `proto/messages.proto`): +Every guest↔host call goes through protobuf envelope types (defined in `proto/message.proto`): - **Request** → `{ data: bytes }` — wraps the serialized request protobuf - **Response** → `{ oneof result { data: bytes, error: string } }` — wraps result or error @@ -69,4 +69,4 @@ make test # no tests yet (all resolver logic tested in confidence-resolver) ## Proto Generation -`build.rs` uses `tonic-build` to compile `proto/messages.proto` (unusual — `tonic-build` is used here even though this isn't a gRPC crate). +`build.rs` uses `tonic-build` to compile `proto/message.proto` (unusual — `tonic-build` is used here even though this isn't a gRPC crate). diff --git a/wasm/rust-guest/CLAUDE.md b/wasm/rust-guest/CLAUDE.md index 9465ce83b..68742b5ea 100644 --- a/wasm/rust-guest/CLAUDE.md +++ b/wasm/rust-guest/CLAUDE.md @@ -2,14 +2,14 @@ ## Overview -The `rust-guest` crate compiles the Confidence resolver to WebAssembly (`wasm32-unknown-unknown`). It is the bridge between host languages (JS, Java, Go, Python, Ruby) and the core resolver. +The `rust-guest` crate compiles the Confidence resolver to WebAssembly (`wasm32-unknown-unknown`). It is the bridge between host languages (JS, Java, Go, and Python) and the core resolver. Ruby resolves remotely and does not use this guest. **Note**: This crate uses `std` (not `no_std`). It uses `std::sync::Arc`, `std::sync::LazyLock`, and the standard allocator — even though it targets WASM. ## Source The entire crate is a single file: `src/lib.rs`. It: -1. Manages global state via `static` items (`RESOLVER_STATE`, `RESOLVE_LOGGER`, `ASSIGN_LOGGER`, `TELEMETRY`) +1. Manages global state via `static` items (resolver state, loggers, telemetry, apply deduplication, and exposure controls) 2. Implements the `Host` trait for `WasmHost` (logging, time, resolve/assign event dispatch) 3. Declares guest functions via `wasm_msg_guest!` macro 4. Declares host imports via `wasm_msg_host!` macro @@ -25,9 +25,11 @@ The `wasm_msg_guest!` macro generates exported functions with the `wasm_msg_gues | `wasm_msg_guest_init_thread` | `InitThreadRequest` | `Void` | Seed RNG for current thread | | `wasm_msg_guest_set_resolver_state` | `SetResolverStateRequest` | `Void` | Load resolver state (protobuf-encoded) | | `wasm_msg_guest_resolve_flags` | `ResolveProcessRequest` | `ResolveProcessResponse` | Resolve flags | +| `wasm_msg_guest_register_resolve` | `RegisterResolveRequest` | `Void` | Record host-measured resolve telemetry | | `wasm_msg_guest_flush_logs` | `Void` | `WriteFlagLogsRequest` | Flush resolve + assign logs (deprecated) | | `wasm_msg_guest_bounded_flush_logs` | `Void` | `WriteFlagLogsRequest` | Flush logs with telemetry delta | | `wasm_msg_guest_bounded_flush_assign` | `Void` | `WriteFlagLogsRequest` | Flush assign logs only (bounded) | +| `wasm_msg_guest_prometheus_snapshot` | `PrometheusSnapshotRequest` | `PrometheusSnapshotResponse` | Render resolver telemetry as Prometheus/OpenMetrics text | | `wasm_msg_guest_apply_flags` | `ApplyFlagsRequest` | `Void` | Apply flags (best-effort logging) | Memory management exports (from `wasm-msg`): @@ -50,5 +52,3 @@ make lint # cargo fmt --check + cargo clippy --target wasm32-unknown-unknown ``` No `test` target — all resolver logic is tested in the `confidence-resolver` crate. This crate is a thin WASM wrapper. - -Typical optimized size: **~450 KB**.