Commit graph

670 commits

Author SHA1 Message Date
jedarden
83c3ecbcac style: apply rustfmt to admin_endpoints.rs metrics callback 2026-05-26 11:04:09 -04:00
jedarden
8e8de0de92 fix(tests): update migration count from 3 to 5 in test_migration_not_reapplied
Added migrations 004 and 005, so the expected count needs to be updated.
2026-05-26 11:00:04 -04:00
jedarden
bec2dba4e8 fix(clippy): resolve uninlined format args and unused imports in benchmarks 2026-05-26 10:56:10 -04:00
jedarden
cf06d48848 feat(bench): add end-to-end and ingest throughput benchmarks
Add two missing performance benchmarks from plan §8:
- end_to_end_bench.rs: measures Miroir vs single-node search latency
  Target: Miroir < 2× single-node latency
- ingest_bench.rs: measures document ingestion throughput
  Target: Miroir > 80% of single-node throughput

Existing benchmarks already cover:
- router_bench.rs: Rendezvous assignment (< 1ms for 10K docs)
- merger_bench.rs: Result merging (< 1ms for 1000 hits)

All benchmarks use simulated latencies for development; integration
tests with live Meilisearch provide real measurements.

Closes: bf-3eb6
2026-05-26 10:45:33 -04:00
jedarden
a7d501dc77 feat(reshard): wire up metrics callback for reshard operations
Previously the reshard orchestrator config had a None metrics_callback,
meaning no Prometheus metrics were emitted during reshard operations.

This commit implements the metrics callback to update:
- miroir_reshard_in_progress: gauge set to 1 during active resharding, 0 when idle/complete/failed
- miroir_reshard_phase: gauge tracking current phase (0=idle, 1=shadow, 2=dual_write, 3=backfill, 4=verify, 5=swapped, 6=cleanup, 7=complete, 8=failed)
- miroir_reshard_documents_backfilled_total: counter incremented with document counts during backfill and later phases

The callback uses the public Metrics API methods (set_reshard_in_progress,
set_reshard_phase, inc_reshard_documents_backfilled) and correctly maps
ReshardPhase enum variants to their corresponding phase numbers.

Closes: bf-4wza
2026-05-26 10:04:28 -04:00
jedarden
9166888a5a fix(task_store): pass now_ms parameter to renew_leader_lease for correctness
Fix the signature of `renew_leader_lease` to accept `now_ms` as a parameter
instead of calling `now_ms()` internally. This ensures time consistency
across the lease renewal check and improves testability.

Changes:
- Add `now_ms: i64` parameter to `TaskStore::renew_leader_lease` trait
- Update all call sites to pass the current time explicitly
- Fix task_pruner to use a short TTL (1s) when releasing the lock
- Update drift_reconciler to pass the current time when renewing

This change prevents potential race conditions where the internal `now_ms()`
call could return a different time than the caller's context, which could
lead to incorrect lease expiration checks.

Gates passed: cargo check, clippy, fmt, nextest (non-Docker tests)
2026-05-26 09:25:41 -04:00
jedarden
e7e73c74b7 feat(ilm): integrate ILM worker into main application
Plan §13.17 ILM (Index Lifecycle Management) worker integration.

- Add ilm_manager and ilm_worker fields to admin_endpoints::AppState
- Create IlmManager when config.ilm.enabled with task store and node addresses
- Spawn ILM worker in main.rs as Mode B background task
- Worker evaluates rollover policies and performs index rollovers when triggers fire
- ILM worker requires leader_election service and task store to operate

Acceptance: ILM worker spawned in main.rs like other Mode B workers,
runs leader-coordinated evaluation loop per plan §14.5.

Closes: bf-509r

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 08:49:31 -04:00
jedarden
5e8eb467f1 feat(search-ui): implement actual rate limiting for session endpoint
- Added rate_limit() method to ErrorResponse for proper HTTP 429 responses
- Added check_detailed() to LocalSearchUiRateLimiter returning (allowed, remaining, reset_after)
- Implemented IP-based rate limiting in mint_session using Redis or local backend
- Extracts client IP from X-Forwarded-For or X-Real-IP headers
- Parses rate limit config (e.g., "60/minute" -> limit=60, window=60s)
- Returns accurate rate limit info (remaining, reset_in) in session response

The rate limit info is now tracked in Redis (miroir:ratelimit:searchui:<ip>)
or in local memory, with proper TTL handling.

Closes: bf-607z
2026-05-26 08:19:25 -04:00
jedarden
d70657171f fix(multi-search): use configured over_fetch_factor instead of hardcoded 1
The multi-search route was hardcoding over_fetch_factor to 1 instead of
using the configured vector_search.over_fetch_factor value. This meant
vector searches in multi-query batches didn't benefit from over-fetching,
leading to incorrect global ranking on sparse semantic matches.

Changes:
- Added HeaderMap parameter to multi_search handler
- Extract X-Miroir-Over-Fetch header for per-request override (plan §13.12)
- Pass over_fetch_factor into the executor closure
- Use over_fetch_factor when building SearchRequest

Closes: bf-5204

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 08:11:17 -04:00
jedarden
ad5877a7e5 feat(reshard): implement backfill phase with pagination and rehashing
Implements plan §13.1 step 3: background streamer pages every live-index
shard using `filter=_miroir_shard={id}`, re-hashes each document under
the new shard count, and writes to the shadow index with the new shard
assignment. Documents are tagged with `origin: "reshard_backfill"` for
CDC event suppression (plan §13.13).

Key changes:
- Added imports for FetchDocumentsRequest, WriteRequest, and json
- Implemented `advance_backfill()` with full pagination loop
- Fetches documents from live index using shard filter
- Extracts primary key from each document
- Re-hashes PK under new shard count using twox-hash
- Injects `_miroir_shard = new_shard_id` into document
- Writes to shadow index with origin tag for CDC suppression
- Tracks progress (total/processed documents, current shard)
- Applies throttling based on configured rate limit
- Made `hash_pk_to_shard()` public for test visibility
- Added tests for document rehashing and executor state

Tests: All 104 reshard tests pass, including new tests for:
- Document rehashing under new shard count
- Executor initialization with correct state
- Backfill progress tracking

Closes: bf-54tf
2026-05-26 08:05:45 -04:00
jedarden
60a59e34e9 style: code formatting cleanup
- Remove trailing blank lines in lib.rs
- Improve line breaking in documents.rs test
- Other minor formatting consistency fixes

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 03:44:20 -04:00
jedarden
4777bb6834 fix(cli): add --version and --help flags to miroir-proxy
Adds clap-based CLI argument parsing so `miroir-proxy --version`
and `miroir-proxy --help` print version/usage and exit instead
of starting the server and hanging.

Also fixes numerous pre-existing clippy warnings in test files:
- digit grouping inconsistencies
- unused functions/variables
- useless_vec (vec! -> array)
- assert!(true) placeholders
- too_many_arguments

Resolves: bf-31ff
2026-05-26 03:02:56 -04:00
jedarden
d10a9ac1fd fix(clippy): resolve unused type parameter, variables, and functions
- Remove unused type parameter S from explain_search function
- Add peer-discovery feature to miroir-proxy Cargo.toml
- Fix unused variables by prefixing with underscore
- Add #[allow(dead_code)] to modules with unused public API functions

Resolves clippy -D warnings for lib and binary targets.
2026-05-26 01:44:28 -04:00
jedarden
a3fdda208c fix(clippy): auto-fix format strings and deprecated IndexMap::remove
Address clippy warnings by:
- Prefixing unused variables with underscore
- Adding #[allow(dead_code)] for intentionally unused helper functions
- Using div_ceil() instead of manual ceiling division
- Simplifying map_or() to is_some_and()
- Fixing type complexity issues with type aliases
- Using .copied() instead of .map(|k| *k)
- Fixing digit grouping inconsistencies (3_600_000)
- Adding #[allow(non_snake_case)] for Meilisearch API-compatible structs
- Removing unnecessary casts
- Fixing await_holding_lock issues

Closes: bf-66nh

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:14:31 -04:00
jedarden
b7f3546c01 fix(clippy): auto-fix format strings and deprecated IndexMap::remove
- Run cargo clippy --fix to apply uninlined format args suggestions
- Fix deprecated IndexMap::remove calls in session_pinning.rs (use shift_remove)
- Various test and source files updated by clippy auto-fix

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 21:31:17 -04:00
jedarden
c3a6ffceb4 fix(tests): import IntoResponse inside cfg(feature) block
The test uses into_response() inside a #[cfg(feature = "axum")] block
but the trait needs to be in scope. Import it inside the cfg block.
2026-05-25 20:29:47 -04:00
jedarden
e5ead74c2f fix(tests): remove unused axum import in p28_api_compatibility
The test imported axum unconditionally but only used it inside a
#[cfg(feature = "axum")] block, causing a compilation error.
Removed the unused import and fixed the unused variable warning.
2026-05-25 20:25:19 -04:00
jedarden
6c00e41678 fix(clippy): fix clippy denied errors
- Fixed infinite loop in cdc.rs overflow buffer trimming by tracking
  bytes_to_remove instead of unmutated current_bytes
- Fixed never_loop warnings in rebalancer_worker by converting
  single-iteration for loops to if-let on first element

These were the only 3 errors that prevented compilation with
-D warnings (207 warnings remain but are not denied by default).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 19:48:18 -04:00
jedarden
f4110b1375 fix(marathon): enforce nextest + command timeouts to prevent loop stalls
The miroir marathon froze 8+ hours when an iteration ran `miroir-proxy --version`
and the binary never exited, holding the loop's stdout pipe open; 42 leaked
acceptance-test processes accumulated over days under bare `cargo test` (no timeout).

- Add .config/nextest.toml with slow-timeout + terminate-after (hung tests are
  killed, not left to wedge the runner)
- instruction.md: replace bare `cargo test` gate with `cargo nextest run`; add
  "Test & process hygiene" section requiring nextest for all runs, hard `timeout`
  wrappers on ad-hoc binary invocations, deterministic test cleanup, and an orphan
  check before iteration exit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 19:45:40 -04:00
jedarden
8ff119ccde fix(vector): handle unstable sort order in convex merge test
The test_merge_convex_basic test had a tie at score 0.7 between doc1
and doc3, but asserted a specific order. Rust's unstable sort makes
this non-deterministic. Updated the test to check that both documents
are present in positions 1-2 regardless of order.

Also applied rustfmt formatting to vector.rs and cdc.rs.
2026-05-25 11:19:50 -04:00
jedarden
0033ad754f fix: remove trailing whitespace and formatting cleanup
- Remove trailing whitespace from multiple files
- Minor formatting fixes across crates
- Net reduction of 69 lines of whitespace

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 09:04:47 -04:00
jedarden
1f894b4dc2 fix(clippy): fix unused imports, variables, and dead code warnings
Fix clippy warnings blocking CI (plan §7 requires -D warnings to pass):
- multi_search.rs: fix format strings, field initialization, unused variables
- anti_entropy_worker.rs: make ModeACoordinator public, prefix unused fields with _, allow dead code for future-use methods
- cdc.rs: allow unused fields and variables (intentionally kept for future use), rename from_str to parse_from_str to avoid std trait confusion
- scatter.rs, mode_b_coordinator.rs, group_sync_worker.rs, mode_a_coordinator.rs: move or remove unused imports
- alias/acceptance_tests.rs, mode_b_acceptance_tests.rs: remove unused imports

These changes fix the initial clippy errors while preserving intentionally-unused code for future use (marked with #[allow(dead_code)] or underscore prefixes).

Closes: bf-ed5n
2026-05-25 08:57:09 -04:00
jedarden
252c9e93be fix(ci): use argo-workflow-executor serviceAccount per plan §7
Closes: bf-52l3
2026-05-25 08:28:58 -04:00
jedarden
3a4c599ca9 fix(helm): enhance Helm connection test with comprehensive endpoint checks
Plan §6 specifies tests/connection-test.yaml for validating Miroir can
connect to Meilisearch. Enhanced the existing test to check:
- /health (basic health)
- /_miroir/ready (dependency health)
- /version (Miroir identification)
- /_miroir/config (topology loaded)

Also fixed clippy warnings:
- Replaced &vec![1u8; 32] with &[1u8; 32] in task_store/sqlite.rs
- Replaced vec![...] with [...] in mode_b_acceptance_tests.rs

Closes: bf-1y7r
2026-05-25 08:27:48 -04:00
jedarden
07156d7354 fix(proxy): formatting fixes in search_ui and scoped_key_rotation
Minor formatting adjustments for consistency:
- Fix indentation in template validation logic
- Fix indentation in timing gate check

These are cosmetic changes that improve code readability
without affecting functionality.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 08:15:43 -04:00
jedarden
a036aa2bca fix(benches): add tokio runtime to dfs_preflight_bench async calls
The benchmark functions were calling .await on async functions
(plan_search_scatter) but were not themselves async. Added tokio
runtime to block on the async calls.

Fixes compilation errors in benchmark code.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 08:04:40 -04:00
jedarden
0c3cdee8c6 fix(tests): update integration.rs for meilisearch_sdk v0.27 API
The meilisearch_sdk v0.27 API changes:
- get_task() expects TaskInfo, not u32
- Client::new() returns Result<Client, Error>
- search().execute() returns SearchResults<T> with SearchResult<T>.result field
- with_facets() expects Selectors<&[&str]>, not &[&str]
- set_synonyms() expects HashMap, not Value
- number_of_documents returns usize, not Option<usize>

Updated integration.rs to match the new API:
- Use TaskInfo directly in wait_for_task()
- Handle Client::new() Result return type
- Access hits via SearchResult.result field
- Use Selectors::Some() for facets
- Use HashMap for synonyms
- Fix lifetime issues with result access

Fixes compilation errors in integration test suite.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 08:00:07 -04:00
jedarden
94a5daa319 docs: add CONTRIBUTING.md for development workflow and code submission
Adds comprehensive contributing guidelines covering:
- Development workflow setup
- Code submission guidelines
- Local testing instructions (unit, integration, chaos, SDK)
- CI/CD pipeline overview
- Documentation standards

Closes: bf-5u89
2026-05-25 07:43:40 -04:00
jedarden
3955d03f87 fix(tests): repair p4_topology_chaos test compilation
Fixed RwLock usage patterns in topology chaos tests. The tests were
calling methods on &Arc<RwLock<Topology>> without properly locking the
RwLock first. Updated to use topology.read().await and
topology.write().await guards.

Also marked nodes as Active after creation to match is_healthy()
expectations (nodes start in Joining state which is not considered
healthy).

Closes: bf-10qf
2026-05-25 07:38:16 -04:00
jedarden
52b69c7b96 docs(readme): add SDK configuration section (Phase 11 §11)
Add before/after code examples for Python, TypeScript, and Go
showing that Miroir integration requires only changing the
endpoint URL — all other SDK code remains unchanged.

Closes: bf-5xge
2026-05-25 07:26:08 -04:00
jedarden
114c9ba48b fix(tests): repair non-deterministic and incorrect vector merge tests
- test_select_adaptive: Set exploration_epsilon=0 for deterministic behavior
- test_merge_convex_basic: Fix expected ordering (doc2 has score 0.8, highest)
- test_merge_rrf_basic: Fix expected score (doc2 has rank 1 in shard 0, rank 0 in shard 1)

All 696 miroir-core tests now pass.

Closes: bf-2zte
2026-05-25 07:20:19 -04:00
jedarden
0b3552ee4f fix(clippy): apply auto-fixes for unused imports and variables
Apply cargo clippy --fix to remove unused imports, prefix unused
variables with underscore, and fix various clippy warnings across
miroir-core, miroir-proxy, and miroir-ctl.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 05:15:22 -04:00
jedarden
465075b5b3 fix(tests): update Redis integration tests for Job struct fields
Add missing Job struct fields (parent_job_id, chunk_index, total_chunks,
created_at) to Redis integration tests. Fix formatting in miroir-ctl
commands and fix unused variable warning in resource_pressure test.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 05:14:38 -04:00
jedarden
2b3f2bfa1c fix(topology): populate shard_count, last_seen_ms, and error fields
- Compute shard_count per node using rendezvous hash assignment
- Compute last_seen_ms from node.last_seen (milliseconds since last health check)
- Populate error field from node.last_error

This completes the plan §10 topology endpoint JSON shape requirements.

Closes: bf-3jy5
2026-05-25 04:40:50 -04:00
jedarden
9f393540a9 fix(tests): move beacon integration tests into correct module
The redis_beacon_idempotency_check and redis_beacon_ttl_cleanup tests
were calling setup_redis_store() from the parent tests module, but the
function is only accessible within the integration submodule. Moved these
tests into the integration submodule and removed incorrect .await calls
(check_and_mark_beacon_event is synchronous per the TaskStore trait).

Closes: miroir-m9q (Phase 6 epic verification)
2026-05-25 04:20:56 -04:00
jedarden
1222e8f606 test(phase-6): add P6.8 multi-pod Kubernetes acceptance tests
Add comprehensive acceptance tests for Phase 6 (Horizontal Scaling + HPA)
as specified in plan §14 Definition of Done.

Files added:
- tests/p6_8_multi_pod_acceptance.sh - Full end-to-end test using kind
- tests/verify_p6_8_templates_direct.sh - Template verification without kind
- tests/verify_p6_8_helm_templates.sh - Helm-based template verification
- tests/p6_8_README.md - Documentation for running the tests

Test coverage:
1. Multi-pod deployment (3 replicas)
2. Peer discovery (headless Service + Downward API)
3. Mode B leader election (exactly one leader, failover)
4. Resource-pressure metrics (all §14.9 metrics)
5. PrometheusRule alerts (all §14.9 alerts)
6. HPA configuration (correct metric types: Pods/External)
7. Resource limits (2 vCPU / 3.75 GB envelope)

The template verification script (verify_p6_8_templates_direct.sh) can be
run in any environment and validates:
- HPA template exists with correct metrics and types
- PrometheusRule has all §14.9 alerts
- Headless Service for peer discovery
- Downward API env vars (POD_NAME, POD_NAMESPACE, POD_IP)
- ServiceMonitor for metrics scraping
- values.schema.json HPA validation

Closes: bf-1976

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:58:37 -04:00
jedarden
cbf0ba12b8 feat(helm): add CDC PVC, Redis auth, and miroir.config template
Implements P8.7: Helm values for CDC PVC, Redis, ESO integration.

Changes:
- Add miroir.config template that generates miroir.yaml from Helm values
- Add miroir.secretName helper for secret name resolution
- Add miroir.redisSecretName helper for Redis secret name resolution
- Add redis.auth section to values.yaml (enabled: true, existingSecret option)
- Update redis-deployment.yaml to support auth with password from secret

The miroir.config template now properly sets taskStore.url to point at
the Redis service when redis.enabled=true, meeting the acceptance criteria
for P8.7.

Note: Redis auth password is passed via MIROIR_REDIS_PASSWORD env var in
the deployment. The Rust code will need to be updated to use this env var
when constructing the Redis connection string.

Closes: miroir-qjt.7
2026-05-25 03:29:02 -04:00
jedarden
0b266bf37e test(miroir-proxy): add P7.6 OpenTelemetry tracing acceptance tests
Adds comprehensive acceptance tests for plan §10 OpenTelemetry tracing:
- Verify tracing.enabled=false returns None (zero overhead)
- Verify default config has tracing disabled
- Verify sample_rate config parsing (default 10%)
- Verify resource attributes (service.name, endpoint, POD_NAME)
- Verify feature flag controls compilation
- Verify shutdown_otel is safe to call multiple times
- Verify span hierarchy exists in scatter path code
- Verify TracingConfig serde round-trip (JSON/TOML)

Also makes the otel module public via lib.rs for test access,
and adds toml as a dev dependency for config parsing tests.

All 15 tests pass. Closes: miroir-afh.6

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:18:27 -04:00
jedarden
6358bdddef feat(cli): add runbook references to all miroir-ctl subcommands (P11.4)
Add after_help text to all 17 miroir-ctl subcommands with links to their
runbook documentation in docs/ctl/*.md.

- status, node, rebalance, task, verify, dump, ui, reshard: core commands
- alias, canary, cdc, explain, shadow, tenant, ttl, key: feature commands

Acceptance criteria met:
✓ Every subcommand has a matching docs/ctl/*.md runbook (pre-existing)
✓ --help mentions where to find runbook (now added)
✓ Runbooks are all under 100 lines each (verified: max 67 lines)

Closes: miroir-uyx.4

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:07:44 -04:00
jedarden
44cc1c68a3 test(mocks): add check_and_mark_beacon_event stub; refactor(multi_search): rename indexUid to index_uid
- Add MockTaskStore::check_and_mark_beacon_event stub (returns true) to acceptance tests
- Rename indexUid → index_uid for consistency in multi_search.rs
- Add plan-gap audit instructions to marathon coding guide

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 02:58:24 -04:00
jedarden
f7043d4518 docs: add troubleshooting cross-links to production and examples guides
Add cross-links from the production deployment guide and Docker Compose
examples README to the main troubleshooting guide and diagnostic playbook.
This completes the cross-linking requirement for P11.5.

Changes:
- docs/onboarding/production.md: Add cross-link to troubleshooting guide
- examples/README.md: Add cross-link to troubleshooting guide

Closes: miroir-uyx.5

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 02:55:06 -04:00
jedarden
17b25e4cf1 feat(analytics): implement beacon idempotency and CDC integration (P5.21.f §13.21)
Implement analytics beacon endpoint with idempotency and CDC integration:

- Add `check_and_mark_beacon_event` to TaskStore trait for idempotency
- Implement for both Redis (HSET with 24h TTL) and SQLite (table with cleanup)
- Add JWT session extraction for session_id in beacon events
- Add server-side event_id generation fallback for old browsers (SHA256 hash)
- Integrate with CDC manager to publish AnalyticsEvents (click_through, latency)
- Respect cdc.emit_internal_writes for latency events
- Add Display impl for JwtValidationError for proper error logging
- Add jwt_decode_with_fallback helper for JWT rotation support
- Add unit tests for beacon idempotency (SQLite and Redis)

Closes: miroir-uhj.21.6
2026-05-25 02:48:55 -04:00
jedarden
451771382e feat(admin-ui): implement login/logout with CSRF token and rate limiting (P5.19.e §13.19)
Implement admin UI login/logout endpoints with CSRF protection, rate limiting,
and session management per plan §13.19.

Login endpoint (POST /_miroir/admin/login):
- Generate session ID and CSRF token
- Store session in task store with CSRF token
- Return sealed session cookie (HttpOnly, Secure, SameSite=Strict)
- Return CSRF token in response body
- Rate limiting: 10/minute per IP with exponential backoff after 5 failures
- Origin validation against admin_ui.allowed_origins

Logout endpoint (POST /_miroir/admin/logout):
- Revoke session in task store
- Clear session cookie (Max-Age=0)
- Redis Pub/Sub propagation for multi-pod deployments

Session endpoint (GET /_miroir/admin/session):
- Validate session and check revocation status
- Return fresh CSRF token on each call
- Check expiration time

Implementation notes:
- Uses task_store trait (supports both Redis and SQLite backends)
- CSRF tokens generated with crypto-random 32-byte values
- Admin key hashed with SHA-256 before storage (never store plaintext)
- Rate limiting supports redis and local backends
- Session TTL configurable via admin_ui.session_ttl_s (default 3600s)

Closes: miroir-uhj.19.5

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 02:24:28 -04:00
jedarden
0c429a42bd feat(admin-ui): add Settings endpoint (P5.19.d §13.19)
Implements GET and PATCH /_miroir/settings endpoints for the Admin UI
Settings section (plan §13.19). The endpoints allow operators to view
and update Miroir's configuration with proper validation.

- GET /_miroir/settings: Returns the full Miroir configuration
- PATCH /_miroir/settings: Updates configuration with restart guards

Restart-required settings (rejected at runtime):
- shards, replication_factor, replica_groups (topology changes)
- nodes (node list changes)
- task_store.backend (backend type changes)
- anti_entropy.enabled (feature flag changes)
- master_key, node_master_key (secrets)

Runtime-updatable settings:
- rebalancer.max_concurrent_migrations
- rebalancer.migration_timeout_s
- query_planner.mode
- session_pinning.enabled
- anti_entropy.schedule

The PATCH endpoint performs deep merge of JSON payloads and validates
the resulting configuration before applying.

Closes: miroir-uhj.19.4
2026-05-25 02:03:38 -04:00
jedarden
c4ed927a50 fix(tests): update meilisearch_sdk API usage for v0.27
The meilisearch_sdk v0.27 API changed:
- get_task() expects types implementing AsRef<u32> (TaskInfo, not u32)
- Client::new() returns Result<Client, Error>
- search().execute() returns SearchResults<T>, not Value

Updated chaos.rs and integration.rs tests to:
- Pass TaskInfo directly to wait_for_task instead of extracting task_uid
- Handle Client::new() Result return type
- Use SearchResults<Value> type annotation for search results
- Import search::SearchResults module

Fixes compilation errors in test suite. Tests compile successfully but
require Docker to actually run (not available in this environment).

Closes: miroir-89x.4
2026-05-25 01:44:23 -04:00
jedarden
6301456750 test(router): configure proptest to run 1024 cases by default (P9.6)
- Add proptest_config(ProptestConfig::with_cases(1024)) to prop_write_targets_count
- Adjust test ranges (shard_count: 1..100, rf: 1..3, nodes_per_group: 3..10) to reduce rejects
- Remove unnecessary prop_assume!(shard_id < shard_count) since write_targets uses shard_id % shard_count internally

All 6 property tests now run at 1024 cases per plan §9.6 acceptance.

Closes: miroir-89x.6

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 01:21:04 -04:00
jedarden
200a638c05 feat(bench): add performance benchmarks and regression gate (P9.5)
Implement plan §8 performance benchmarks with criterion:

- Fixed merger_bench.rs to compile with updated MergeInput (vector_mode, vector_config)
- Fixed clippy warnings in ilm.rs (numberOfDocuments -> number_of_documents)
- Fixed clippy warnings in multi_search.rs (indexUid -> index_uid)
- Added docs/benchmarks.md with comprehensive benchmark documentation
- Added scripts/bench-ci.sh for CI benchmark runner
- Added scripts/bench-compare.sh for regression gate (>20% slowdown detection)

Benchmarks verified:
- router_bench: Rendezvous ~384 µs for 10K docs (target: <1 ms) 
- merger_bench: Merger ~1.07 ms for 1000 hits/3 shards (target: <1 ms) ⚠️
- integration_bench: E2E latency and ingest throughput (require docker-compose)

Closes: miroir-89x.5

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 00:44:33 -04:00
jedarden
e19f0c8137 feat(admin-ui): add session cookie authentication support for embedded SPA
Updated `serve_admin_ui` to accept requests authenticated via admin
session cookie (set by `/admin/login`), in addition to the existing
X-Admin-Key and Authorization: Bearer header methods.

The auth middleware already unseals the session cookie and sets the
`AdminSessionId` extension - the UI handler now checks for this extension
to allow cookie-authenticated requests through.

Added comprehensive unit tests for:
- X-Admin-Key authentication
- Bearer token authentication
- Session cookie authentication (via extension)
- File serving with proper cache headers
- 404 for missing files

The embedded admin UI assets are ~35 KB gzipped (well under the 100 KB
requirement). Session sealing, CSRF, and cross-pod session invalidation
were already implemented in prior work.

Closes: miroir-uhj.19
2026-05-25 00:18:46 -04:00
jedarden
56585972ca fix(release): strip quotes from Chart.yaml appVersion in release-ready-check
The appVersion field in Chart.yaml has quotes around the value (e.g.
appVersion: "0.1.0"), which the release-ready-check.sh script was
including in the parsed value. This caused false positive failures
when comparing Cargo.toml version (0.1.0) with Chart.yaml appVersion
("0.1.0").

Fix by piping to tr -d '"' to strip the quotes.

Closes: miroir-qjt.6 (P8.6 Release mechanics)

All release mechanics acceptance criteria verified:
- bump-version.sh atomically updates all 3 files
- miroir-release.yaml handles tag-triggered releases
- Pre-release tags skip :latest and float tags
- release-ready-check.sh now correctly validates version sync

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 00:09:16 -04:00
jedarden
9d29d757c7 feat(admin-ui): add 2PC settings preview endpoint and UI integration
Implements P5.19.b §13.19 - Indexes + Aliases sections with LIVE 2PC preview.

Backend changes:
- Add POST /indexes/{index}/settings preview endpoint
- Returns current vs proposed settings with SHA256 fingerprints
- Shows node targets, version info, and diff summary
- Displays full two-phase flow (propose/verify/commit) details
- Export compute_settings_diff for testing

Frontend changes:
- Update previewSettingsChanges() to call new preview endpoint
- Display current/proposed fingerprints, version info
- Show node targets and two-phase flow steps
- Render structured diff (added/removed/modified)

Tests:
- Add p13_19_admin_ui_2pc_preview.rs acceptance tests
- Verify fingerprint computation, diff detection, node targets

Closes: miroir-uhj.19.2
2026-05-25 00:03:35 -04:00