#!/usr/bin/env python3 """ Minimal k_server service for Phase 5/5.5 bring-up. Behavior: - Exposes a protected monotonic counter endpoint. - Accepts only requests from k_proxy via a shared proxy token header. - Uses thread-safe counter increments. """ from __future__ import annotations import argparse import json import ssl import threading import time from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from typing import Any from urllib.parse import urlparse class ServerState: def __init__(self, proxy_token: str): self.proxy_token = proxy_token self.counter = 0 self.lock = threading.Lock() def next_counter(self) -> int: with self.lock: self.counter += 1 return self.counter class Handler(BaseHTTPRequestHandler): state: ServerState def _json(self, status: int, payload: dict[str, Any]) -> None: body = json.dumps(payload).encode("utf-8") self.send_response(status) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def _is_proxy_authorized(self) -> bool: return self.headers.get("X-Proxy-Token") == self.state.proxy_token def do_GET(self) -> None: # noqa: N802 path = urlparse(self.path).path if path == "/health": self._json( 200, { "ok": True, "service": "k_server", "time": int(time.time()), }, ) return self.send_error(404) def do_POST(self) -> None: # noqa: N802 path = urlparse(self.path).path if path != "/resource/counter": self.send_error(404) return if not self._is_proxy_authorized(): self._json(401, {"ok": False, "error": "unauthorized proxy"}) return value = self.state.next_counter() self._json( 200, { "ok": True, "resource": "counter", "value": value, "time": int(time.time()), }, ) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Run k_server counter service") parser.add_argument("--host", default="127.0.0.1") parser.add_argument("--port", type=int, default=8780) parser.add_argument("--tls-certfile", help="PEM certificate chain for HTTPS listener") parser.add_argument("--tls-keyfile", help="PEM private key for HTTPS listener") parser.add_argument( "--proxy-token", default="dev-proxy-token", help="Shared token expected in X-Proxy-Token from k_proxy", ) return parser.parse_args() def main() -> int: args = parse_args() if bool(args.tls_certfile) != bool(args.tls_keyfile): raise SystemExit("Both --tls-certfile and --tls-keyfile are required to enable HTTPS") state = ServerState(proxy_token=args.proxy_token) Handler.state = state server = ThreadingHTTPServer((args.host, args.port), Handler) scheme = "http" if args.tls_certfile: context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) context.load_cert_chain(certfile=args.tls_certfile, keyfile=args.tls_keyfile) server.socket = context.wrap_socket(server.socket, server_side=True) scheme = "https" print(f"k_server listening on {scheme}://{args.host}:{args.port}") server.serve_forever() return 0 if __name__ == "__main__": raise SystemExit(main())