From de1dd592176e7f803a72781bfad4d13775e8fef4 Mon Sep 17 00:00:00 2001 From: Shariq Naiyer Date: Fri, 23 Jan 2026 12:42:35 -0700 Subject: [PATCH 1/2] feat: add versioning to api endpoints --- src/lean_spec/subspecs/api/__init__.py | 2 +- src/lean_spec/subspecs/api/client.py | 2 +- src/lean_spec/subspecs/api/server.py | 14 +++++++------- tests/lean_spec/subspecs/api/test_server.py | 12 ++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lean_spec/subspecs/api/__init__.py b/src/lean_spec/subspecs/api/__init__.py index edd09430..c9fa44d2 100644 --- a/src/lean_spec/subspecs/api/__init__.py +++ b/src/lean_spec/subspecs/api/__init__.py @@ -2,7 +2,7 @@ API server module for checkpoint sync and node status endpoints. Provides HTTP endpoints for: -- /lean/states/finalized - Serve finalized checkpoint state as SSZ +- /lean/v0/states/finalized - Serve finalized checkpoint state as SSZ - /health - Health check endpoint Also provides a client for checkpoint sync: diff --git a/src/lean_spec/subspecs/api/client.py b/src/lean_spec/subspecs/api/client.py index 70b98c64..97422614 100644 --- a/src/lean_spec/subspecs/api/client.py +++ b/src/lean_spec/subspecs/api/client.py @@ -35,7 +35,7 @@ DEFAULT_TIMEOUT = 60.0 """HTTP request timeout in seconds. Large states may take time to transfer.""" -FINALIZED_STATE_ENDPOINT = "/lean/states/finalized" +FINALIZED_STATE_ENDPOINT = "/lean/v0/states/finalized" """API endpoint for fetching finalized state. Follows Beacon API conventions.""" diff --git a/src/lean_spec/subspecs/api/server.py b/src/lean_spec/subspecs/api/server.py index 7911fbd0..c6281945 100644 --- a/src/lean_spec/subspecs/api/server.py +++ b/src/lean_spec/subspecs/api/server.py @@ -2,8 +2,8 @@ API server for checkpoint sync, node status, and metrics endpoints. Provides HTTP endpoints for: -- /lean/states/finalized - Serve finalized checkpoint state as SSZ -- /lean/states/justified - Return latest justified checkpoint information +- /lean/v0/states/finalized - Serve finalized checkpoint state as SSZ +- /lean/v0/states/justified - Return latest justified checkpoint information - /health - Health check endpoint - /metrics - Prometheus metrics endpoint @@ -100,8 +100,8 @@ async def start(self) -> None: [ web.get("/health", _handle_health), web.get("/metrics", _handle_metrics), - web.get("/lean/states/finalized", self._handle_finalized_state), - web.get("/lean/states/justified", self._handle_justified), + web.get("/lean/v0/states/finalized", self._handle_finalized_state), + web.get("/lean/v0/states/justified", self._handle_justified_state), ] ) @@ -142,7 +142,7 @@ async def _handle_finalized_state(self, _request: web.Request) -> web.Response: """ Handle finalized checkpoint state endpoint. - Serves the finalized state as SSZ binary at /lean/states/finalized. + Serves the finalized state as SSZ binary at /lean/v0/states/finalized. This endpoint is used for checkpoint sync - clients can download the finalized state to bootstrap quickly instead of syncing from genesis. """ @@ -166,11 +166,11 @@ async def _handle_finalized_state(self, _request: web.Request) -> web.Response: return web.Response(body=ssz_bytes, content_type="application/octet-stream") - async def _handle_justified(self, _request: web.Request) -> web.Response: + async def _handle_justified_state(self, _request: web.Request) -> web.Response: """ Handle latest justified checkpoint endpoint. - Returns the latest justified checkpoint information as JSON at /lean/states/justified. + Returns the latest justified checkpoint information as JSON at /lean/v0/states/justified. This provides the slot number and root hash of the most recent justified checkpoint, which is useful for monitoring consensus progress and fork choice state. diff --git a/tests/lean_spec/subspecs/api/test_server.py b/tests/lean_spec/subspecs/api/test_server.py index 2fbfe6e8..987270f1 100644 --- a/tests/lean_spec/subspecs/api/test_server.py +++ b/tests/lean_spec/subspecs/api/test_server.py @@ -90,7 +90,7 @@ async def run_test() -> None: class TestFinalizedStateEndpoint: - """Tests for the /lean/states/finalized endpoint behavior.""" + """Tests for the /lean/v0/states/finalized endpoint behavior.""" def test_returns_503_when_store_not_initialized(self) -> None: """Endpoint returns 503 Service Unavailable when store is not set.""" @@ -103,7 +103,7 @@ async def run_test() -> None: try: async with httpx.AsyncClient() as client: - response = await client.get("http://127.0.0.1:15054/lean/states/finalized") + response = await client.get("http://127.0.0.1:15054/lean/v0/states/finalized") assert response.status_code == 503 @@ -124,7 +124,7 @@ async def run_test() -> None: try: async with httpx.AsyncClient() as client: - response = await client.get("http://127.0.0.1:15056/lean/states/finalized") + response = await client.get("http://127.0.0.1:15056/lean/v0/states/finalized") assert response.status_code == 200 assert response.headers["content-type"] == "application/octet-stream" @@ -143,7 +143,7 @@ async def run_test() -> None: class TestJustifiedEndpoint: - """Tests for the /lean/states/justified endpoint behavior.""" + """Tests for the /lean/v0/states/justified endpoint behavior.""" def test_returns_503_when_store_not_initialized(self) -> None: """Endpoint returns 503 Service Unavailable when store is not set.""" @@ -156,7 +156,7 @@ async def run_test() -> None: try: async with httpx.AsyncClient() as client: - response = await client.get("http://127.0.0.1:15057/lean/states/justified") + response = await client.get("http://127.0.0.1:15057/lean/v0/states/justified") assert response.status_code == 503 @@ -177,7 +177,7 @@ async def run_test() -> None: try: async with httpx.AsyncClient() as client: - response = await client.get("http://127.0.0.1:15058/lean/states/justified") + response = await client.get("http://127.0.0.1:15058/lean/v0/states/justified") assert response.status_code == 200 assert "application/json" in response.headers["content-type"] From c8bd52f33b6986b22d4b18ae237f6cbd15c25990 Mon Sep 17 00:00:00 2001 From: Shariq Naiyer Date: Fri, 23 Jan 2026 12:52:28 -0700 Subject: [PATCH 2/2] feat: version health as well --- src/lean_spec/subspecs/api/__init__.py | 2 +- src/lean_spec/subspecs/api/server.py | 4 ++-- tests/lean_spec/subspecs/api/test_server.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lean_spec/subspecs/api/__init__.py b/src/lean_spec/subspecs/api/__init__.py index c9fa44d2..c2314786 100644 --- a/src/lean_spec/subspecs/api/__init__.py +++ b/src/lean_spec/subspecs/api/__init__.py @@ -3,7 +3,7 @@ Provides HTTP endpoints for: - /lean/v0/states/finalized - Serve finalized checkpoint state as SSZ -- /health - Health check endpoint +- /lean/v0/health - Health check endpoint Also provides a client for checkpoint sync: - fetch_finalized_state: Download finalized state from a node diff --git a/src/lean_spec/subspecs/api/server.py b/src/lean_spec/subspecs/api/server.py index c6281945..f83bc01b 100644 --- a/src/lean_spec/subspecs/api/server.py +++ b/src/lean_spec/subspecs/api/server.py @@ -4,7 +4,7 @@ Provides HTTP endpoints for: - /lean/v0/states/finalized - Serve finalized checkpoint state as SSZ - /lean/v0/states/justified - Return latest justified checkpoint information -- /health - Health check endpoint +- /lean/v0/health - Health check endpoint - /metrics - Prometheus metrics endpoint This matches the checkpoint sync API implemented in zeam. @@ -98,7 +98,7 @@ async def start(self) -> None: app = web.Application() app.add_routes( [ - web.get("/health", _handle_health), + web.get("/lean/v0/health", _handle_health), web.get("/metrics", _handle_metrics), web.get("/lean/v0/states/finalized", self._handle_finalized_state), web.get("/lean/v0/states/justified", self._handle_justified_state), diff --git a/tests/lean_spec/subspecs/api/test_server.py b/tests/lean_spec/subspecs/api/test_server.py index 987270f1..d0aa3b81 100644 --- a/tests/lean_spec/subspecs/api/test_server.py +++ b/tests/lean_spec/subspecs/api/test_server.py @@ -62,7 +62,7 @@ def test_store_getter_provides_access_to_store(self, base_store: Store) -> None: class TestHealthEndpoint: - """Tests for the /health endpoint behavior.""" + """Tests for the /lean/v0/health endpoint behavior.""" def test_returns_healthy_status_json(self) -> None: """Health endpoint returns JSON with healthy status.""" @@ -75,7 +75,7 @@ async def run_test() -> None: try: async with httpx.AsyncClient() as client: - response = await client.get("http://127.0.0.1:15052/health") + response = await client.get("http://127.0.0.1:15052/lean/v0/health") assert response.status_code == 200 data = response.json()