Commit graph

9 commits

Author SHA1 Message Date
jedarden
fca081e1bd Integrate MeilisearchError into proxy (IntoResponse, auth middleware) + telemetry
- Add axum feature flag to miroir-core with IntoResponse impl for MeilisearchError
- Refactor auth middleware to use MeilisearchError::new() + MiroirCode instead of
  manual JSON construction, ensuring consistent error shape across all auth errors
- Add proxy error.rs re-export alias for ApiError
- Implement full telemetry middleware with Prometheus metrics (request duration,
  in-flight gauge, scatter counters, node health)
- Reorder middleware layers: auth before telemetry so 401s are also instrumented

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 05:21:09 -04:00
jedarden
625e414b6c Implement bearer-token dispatch chain (plan §5 rules 0-5) + X-Admin-Key
Add deterministic bearer-token dispatch with five rules:
- Rule 0: dispatch-exempt endpoints skip all auth (metrics, locale, login,
  session, SPA)
- Rule 1: JWT-shape probe stub (Phase 5 will add full validation)
- Rule 2: admin-path (/__miroir/*) matches only admin_key
- Rule 3: non-admin paths match only master_key
- Rule 4: mismatch returns 401 miroir_invalid_auth

Also adds X-Admin-Key header short-circuit for admin endpoints,
constant-time comparison via subtle::ConstantTimeEq, rate-limit hook
types (Phase 2 in-memory stub), and 54 unit tests covering all
acceptance criteria.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 05:11:57 -04:00
jedarden
b2490ea64d Phase 1 Core Routing: validate and fix compilation
All Phase 1 DoD criteria verified:
- Rendezvous assignment deterministic (test_determinism)
- Reshuffle bound on add: ≤2×(1/4) edges (test_reshuffle_bound_on_add)
- Uniformity: 64/3/RF=1 → 17-26 shards/node (test_uniformity)
- RF placement stability on add/remove (test_rf2_placement_stability)
- write_targets returns exactly RG×RF nodes, one per group
- query_group distributes evenly (chi-square test)
- covering_set with intra-group replica rotation
- Merger passes merge/facet/limit/stripping tests
- miroir-core ≥90% line coverage (92.07% via cargo-tarpaulin --lib)

Fixes:
- scatter.rs: NodeId::new(&str) → NodeId::new("...".into()) for type mismatch
- merger.rs: add P12.OP4 RRF skew validation tests
- config.rs: fix test to use redis backend for file loading
- proxy: wire up client module, add indexes route stubs

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 03:22:33 -04:00
jedarden
8d332f247e P1: Finalize core routing — tighten uniformity bounds, fix warnings, update deps
Phase 1 core routing (rendezvous hash, topology, covering set, RRF merger) is
already implemented and tested. This commit finalizes:

- Tighten router uniformity test to verified range 17–26 (DoD §8)
- Suppress async_fn_in_trait warning in scatter NodeClient trait
- Suppress dead_code warning for test helper make_hit_ranked
- Downgrade serde_with/darling to Rust 1.87-compatible versions

All 148 tests pass (122 unit + 14 chaos + 12 proptest).
Line coverage: router 96.5%, topology 93.0%, scatter 94.0%, merger 96.3%.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 01:04:29 -04:00
jedarden
612e7ce0ea P1.5: Implement scatter module with covering-set construction + dispatch trait
- Add NodeClient trait for HTTP calls to Meilisearch nodes (seam between pure miroir-core and networked miroir-proxy)
- Add ScatterPlan struct containing chosen_group, target_shards, shard_to_node mapping, deadline_ms, hedging_eligible
- Implement plan_search_scatter() pure function that constructs the covering set without I/O
- Implement execute_scatter() async function that fans out to nodes with partial-failure handling
- Add MockNodeClient for testing with pre-programmed responses/errors
- Add unit tests for plan construction, query group rotation, shard-to-node mapping, hedging eligibility, and scatter execution

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 00:20:29 -04:00
jedarden
21aebb386c P0: Fix clippy warnings and remove broken openraft dep for clean CI
- Add Default impls for TaskStateMachine and RaftTaskRegistry (clippy::new_without_default)
- Remove openraft dep that fails on stable Rust 1.87 (validit uses let_chains)
- Silence dead_code warnings in raft_proto benchmark module
- Add autobenches = false to miroir-core Cargo.toml
- Update Cargo.lock

All Phase 0 DoD criteria pass: build, test (73), clippy, fmt, musl release.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 23:38:24 -04:00
jedarden
e47c1c2f73 P12.OP3: Validate 2× transient load caveat and add CLI schedule window guard
- Add resharding load simulation model with real router hash functions
- Benchmark confirms storage amplification is exactly 2.0× and dual-write
  amplification is exactly 2.0× across all test matrix scenarios (1KB/10GB,
  10KB/100GB, 1MB/1TB), with hash distribution CV < 5% in all cases
- CLI window guard: resharding.allowed_windows config restricts resharding
  to named time windows (e.g. "02:00-06:00 UTC"), CLI refuses outside
  windows without --force
- Integration tests confirm rejection outside window, --force override,
  no-restriction mode, and disabled config handling

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 22:00:57 -04:00
jedarden
9b5cf0ddcd P0.3: Scaffold miroir-proxy crate
- Added Cargo.toml with axum, tokio, reqwest, serde, tracing, prometheus
- Created main.rs: binds :7700 (main API) and :9090 (metrics)
- Route handler stubs: documents, search, indexes, settings, tasks, health, admin
- auth.rs: bearer-token dispatch skeleton (client/admin token kinds)
- middleware.rs: tracing/logging + Prometheus middleware stubs
- Fixed miroir-core/migration.rs: Display impls, Instant serialization, borrow fixes

Acceptance:
- Binary builds successfully
- Health endpoint returns {"status":"available"}
- Stripped binary: 2.3 MB (< 20 MB target)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 20:57:58 -04:00
jedarden
409f952f59 Add repo hygiene: LICENSE, CHANGELOG, .gitignore
- LICENSE: MIT (per plan §12)
- CHANGELOG.md: Keep a Changelog 1.1.0 skeleton with [Unreleased]
  and [0.1.0] sections matching the awk extractor from plan §7
- .gitignore: Rust target/, editor junk; Cargo.lock kept in VCS

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 20:47:36 -04:00