feat(perception): add scoped stream control and bounded clock observations

This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 19:41:43 +03:00
parent 0cc6b96306
commit d7b8989e43
6 changed files with 1160 additions and 2 deletions
@@ -0,0 +1,312 @@
"""CPU-only clock/grant route evidence; NOT a model/freshness qualification.
One bootstrap ticket/certificate copied via authenticated SSH, subsequent data
grants delivered only by TLS Poll. Source events/resume proof remain synthetic;
real monotonic clock observations use explicit CONDITIONAL error/rate budgets.
"""
import argparse
import asyncio
import json
import os
import resource
import secrets
import subprocess
import sys
import threading
import time
from dataclasses import asdict
from datetime import UTC, datetime
from hashlib import sha256
from pathlib import Path
import grpc
from grpc_transport_probe import document, identity, until
from k1link.compute.live_perception import LiveIngressEvent
from k1link.perception.realtime_contract import StreamStart
from k1link.perception.streaming_clock import ClockMappingError, ClockWindow
from k1link.perception.streaming_continuity import ResumeEvidence
from k1link.perception.streaming_control_grpc import (
ControlTicket,
StreamControlClient,
StreamControlEndpoint,
)
from k1link.perception.streaming_grpc import GrpcStreamClient, GrpcStreamEndpoint
from k1link.perception.streaming_lifecycle import StreamingLifecycle
from k1link.perception.streaming_queue import StreamMailbox
async def server(root):
private = root / "private"
private.mkdir(mode=0o700, exist_ok=True)
cert, key, bootstrap = [private / x for x in ("cert.pem", "key.pem", "bootstrap.json")]
subprocess.run(
[
"openssl",
"req",
"-x509",
"-newkey",
"rsa:2048",
"-nodes",
"-days",
"1",
"-subj",
"/CN=localhost",
"-keyout",
str(key),
"-out",
str(cert),
],
check=True,
capture_output=True,
timeout=10,
)
run = StreamingLifecycle(
identity(),
root / "lease",
StreamMailbox(),
threading.Event(),
recover_input=True,
source_clock_ns=lambda: 1_000_000_000,
)
resident = run.spawn(
lambda: subprocess.Popen(
[sys.executable, "-c", "import time; time.sleep(180)"],
start_new_session=True,
)
)
run.ready()
pulse_stop = threading.Event()
def pulse():
while not pulse_stop.wait(0.1):
run.renew(identity())
pulse_thread = threading.Thread(target=pulse, daemon=True)
pulse_thread.start()
received = []
def consume(event):
item = {
"sequence": event.ingress_sequence,
"sha256": sha256(event.payload).hexdigest(),
"bytes": len(event.payload),
"resident_pid": resident.pid,
}
received.append(item)
if len(received) > 8:
raise ValueError("bounded count exceeded")
endpoint.publish(
run.continuity.epoch, event.ingress_sequence, json.dumps(item, sort_keys=True).encode()
)
endpoint = GrpcStreamEndpoint(run, consume, lambda _: None)
pending = [endpoint.issue(run.continuity.epoch, "synthetic-capture", 1)]
ticket = ControlTicket(identity(), secrets.token_hex(32))
clock_id = "worker-process-" + secrets.token_hex(12)
control = StreamControlEndpoint(
ticket,
lambda: pending[0] if endpoint.grant is not None else None,
clock_id=clock_id,
)
report = {
"utc": datetime.now(UTC).isoformat(),
"started_monotonic_ns": str(time.monotonic_ns()),
"model_count": 0,
"synthetic_source_and_resume": True,
"real_time_qualified": False,
"actuation_allowed": False,
"received": received,
"clock_id": clock_id,
"grpc_version": grpc.__version__,
}
rpc = None
try:
rpc, _ = await endpoint.serve(
"0.0.0.0:50061",
certificate=cert.read_bytes(),
private_key=key.read_bytes(),
control_handlers=(control.handler(),),
)
document(
bootstrap,
{
"activation": identity().to_dict(),
"token": ticket.token,
"remote_clock_id": clock_id,
},
)
bootstrap.chmod(0o600)
await until(lambda: len(received) == 4 and endpoint.active is None, seconds=90)
report["first_transport"] = endpoint.last
report["before_gap"] = run.snapshot()
began = time.monotonic_ns()
await asyncio.sleep(2.2)
report["gap_ns"] = str(time.monotonic_ns() - began)
assert resident.poll() is None and run.continuity.phase == "waiting"
report["after_gap"] = run.snapshot()
epoch = run.begin_input(identity())
run.resume_input(epoch, ResumeEvidence(*([1_000_000_000] * 4), True), lambda: None)
pending[0] = endpoint.issue(epoch, "synthetic-capture", 1)
await until(lambda: len(received) == 8 and run.mailbox.done, seconds=30)
endpoint.finish_results(epoch)
await until(lambda: endpoint.active is None)
report["second_transport"] = endpoint.last
report["passed"] = resident.poll() is None
report["peak_mailbox_bytes"] = run.mailbox.peak_bytes
finally:
if rpc:
await rpc.stop(0)
pulse_stop.set()
pulse_thread.join(1)
report["closed"] = run.close()
report["resident_reaped"] = resident.poll() is not None
report["final_mailbox_bytes"] = run.mailbox.bytes
report["control_accepted"] = control.accepted
report["control_rejected"] = control.rejected
report["rss_max_kib"] = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
report["finished_monotonic_ns"] = str(time.monotonic_ns())
document(root / "server-report.json", report)
for path in (key, bootstrap):
path.unlink(missing_ok=True)
async def client(root, target):
bootstrap = root / "bootstrap.json"
value = json.loads(bootstrap.read_text())
ticket = ControlTicket(StreamStart.from_dict(value["activation"]), value["token"])
local_clock_id = "mac-process-" + secrets.token_hex(12)
control = StreamControlClient(
target, (root / "cert.pem").read_bytes(), ticket, clock_id=local_clock_id
)
mapping = ClockWindow(
local_clock_id,
value["remote_clock_id"],
rate_ppm=500,
timestamp_error_ns=50_000,
maximum_age_ns=2_000_000_000,
)
rows, grants, replies, errors = [], [], [], []
report = {
"utc": datetime.now(UTC).isoformat(),
"started_monotonic_ns": str(time.monotonic_ns()),
"schema_version": "missioncore.grpc-control-clock-probe/v1",
"observations": rows,
"grants": grants,
"replies": replies,
"errors": errors,
"model_count": 0,
"synthetic_source_and_resume": True,
"real_time_qualified": False,
"one_way_frame_age_measured": False,
"actuation_allowed": False,
"conditional_relative_rate_ppm": 500,
"timestamp_error_budget_ns": "50000",
"maximum_mapping_age_ns": "2000000000",
"admission_uncertainty_ns": "5000000",
"path": "TLS gRPC over existing Mac-Worker authenticated SSH/Tailscale route",
}
async def poll(phase):
for _ in range(4):
try:
probe, access = await control.poll()
break
except grpc.aio.AioRpcError as exc:
errors.append({"phase": phase, "code": exc.code().name})
await asyncio.sleep(0.1)
else:
raise RuntimeError("bounded control polling failed")
bounds = mapping.add(probe)
now = time.monotonic_ns()
uncertainty = bounds.uncertainty_ns(now)
raw = {k: str(v) if type(v) is int else v for k, v in asdict(probe).items()}
rows.append(
{
"phase": phase,
"probe": raw,
"bounds": bounds.to_dict(),
"evaluated_at_ns": str(now),
"uncertainty_ns": str(uncertainty),
"admitted_5ms": uncertainty <= 5_000_000,
}
)
return access
try:
previous = None
for phase in (1, 2):
if phase == 2:
await asyncio.sleep(2.3)
try:
mapping.current(time.monotonic_ns())
except ClockMappingError:
report["mapping_expired_during_gap"] = True
else:
raise AssertionError("old clock mapping survived expiry")
access = None
for _ in range(32):
candidate = await poll(phase)
if candidate is not None:
access = candidate
await asyncio.sleep(0.05)
assert access is not None and (previous is None or access.epoch != previous.epoch)
assert previous is None or access.token != previous.token
grants.append(
{
"phase": phase,
"epoch": access.epoch.to_dict(),
"token_changed": previous is not None,
"delivery": "authenticated-control-poll",
}
)
stream = GrpcStreamClient(target, (root / "cert.pem").read_bytes(), access)
try:
await stream.open()
for sequence in range((phase - 1) * 4 + 1, phase * 4 + 1):
payload = bytes([sequence]) * 32768
event = LiveIngressEvent(
sequence,
"synthetic-capture",
1,
"lidar",
"lidar",
sequence,
1_799_999_999_123_456_789,
1_000_000_000,
payload,
)
started = time.monotonic_ns()
await stream.send(event)
result = await stream.receive()
elapsed = time.monotonic_ns() - started
returned = json.loads(result[1])
assert (
result[0] == sequence and returned["sha256"] == sha256(payload).hexdigest()
)
replies.append({**returned, "round_trip_ns": str(elapsed)})
assert await poll(phase) is None # Control still works during Exchange.
await asyncio.sleep(0.05)
if phase == 2:
await stream.end()
assert await stream.receive() is None
finally:
await stream.close()
previous = access
report["passed"] = True
finally:
await control.close()
report["finished_monotonic_ns"] = str(time.monotonic_ns())
document(root / "client-report.json", report)
bootstrap.unlink(missing_ok=True)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("mode", choices=("server", "client"))
parser.add_argument("root", type=Path)
parser.add_argument("--target", default="localhost:18561")
args = parser.parse_args()
os.umask(0o077)
asyncio.run(server(args.root) if args.mode == "server" else client(args.root, args.target))
+225
View File
@@ -0,0 +1,225 @@
"""Bounded, conditional mapping of two process-scoped monotonic clocks.
Four timestamps bound remote-minus-local offset WITHOUT symmetric-path claims.
Intersect recent intervals, widened for an explicit relative-rate/error budget.
Neither OS clock is adjusted. The rate/error envelope is an assumption supplied
by the controller, not a hardware accuracy certificate inferred from low RTT.
"""
from __future__ import annotations
from collections import deque
from dataclasses import dataclass
from .streaming_wire import identifier
class ClockMappingError(ValueError):
pass
def _stamp(value: int) -> None:
if type(value) is not int or not 0 <= value < 2**63:
raise ClockMappingError("invalid monotonic timestamp")
def _drift(duration_ns: int, ppm: int) -> int:
return (duration_ns * ppm + 999_999) // 1_000_000
@dataclass(frozen=True)
class ClockProbe:
local_clock_id: str
remote_clock_id: str
nonce: str
local_send_ns: int
remote_receive_ns: int
remote_send_ns: int
local_receive_ns: int
def __post_init__(self) -> None:
for value in (self.local_clock_id, self.remote_clock_id, self.nonce):
identifier(value)
for stamp in (
self.local_send_ns,
self.remote_receive_ns,
self.remote_send_ns,
self.local_receive_ns,
):
_stamp(stamp)
if (
not 0 <= self.local_receive_ns - self.local_send_ns <= 2_000_000_000
or self.remote_send_ns < self.remote_receive_ns
):
raise ClockMappingError("clock probe ordering/deadline violated")
def bounds_at(
self, local_ns: int, *, rate_ppm: int, timestamp_error_ns: int
) -> tuple[int, int]:
_stamp(local_ns)
if local_ns < self.local_receive_ns:
raise ClockMappingError("clock observation is from the future")
# Any outbound/inbound asymmetry is inside these bounds. Remote receive
# and send occur between local send and receive; widen across the whole
# interval, not just elapsed time after receipt.
widen = _drift(local_ns - self.local_send_ns, rate_ppm) + 2 * timestamp_error_ns
return (
self.remote_send_ns - self.local_receive_ns - widen,
self.remote_receive_ns - self.local_send_ns + widen,
)
@dataclass(frozen=True)
class ClockBounds:
local_clock_id: str
remote_clock_id: str
measured_at_ns: int
expires_at_ns: int
offset_lower_ns: int
offset_upper_ns: int
rate_ppm: int
timestamp_error_ns: int
samples: int
def __post_init__(self) -> None:
for value in (self.local_clock_id, self.remote_clock_id):
identifier(value)
_stamp(self.measured_at_ns)
_stamp(self.expires_at_ns)
if (
self.measured_at_ns >= self.expires_at_ns
or type(self.offset_lower_ns) is not int
or type(self.offset_upper_ns) is not int
or self.offset_lower_ns > self.offset_upper_ns
or type(self.rate_ppm) is not int
or not 1 <= self.rate_ppm <= 1000
or type(self.timestamp_error_ns) is not int
or not 1 <= self.timestamp_error_ns <= 1_000_000
or type(self.samples) is not int
or not 1 <= self.samples <= 16
):
raise ClockMappingError("invalid clock bounds")
def offset_at(self, local_ns: int) -> tuple[int, int]:
_stamp(local_ns)
if not self.measured_at_ns <= local_ns < self.expires_at_ns:
raise ClockMappingError("clock mapping expired or local clock regressed")
extra = _drift(local_ns - self.measured_at_ns, self.rate_ppm)
return self.offset_lower_ns - extra, self.offset_upper_ns + extra
def uncertainty_ns(self, local_ns: int) -> int:
lower, upper = self.offset_at(local_ns)
return (upper - lower + 1) // 2
def require(self, local_ns: int, maximum_uncertainty_ns: int = 5_000_000) -> None:
if type(maximum_uncertainty_ns) is not int or maximum_uncertainty_ns <= 0:
raise ClockMappingError("invalid uncertainty gate")
if self.uncertainty_ns(local_ns) > maximum_uncertainty_ns:
raise ClockMappingError("clock uncertainty exceeds admission budget")
def to_dict(self) -> dict[str, object]:
return {
"schema_version": "missioncore.monotonic-clock-bounds/v1",
"local_clock_id": self.local_clock_id,
"remote_clock_id": self.remote_clock_id,
"measured_at_ns": str(self.measured_at_ns),
"expires_at_ns": str(self.expires_at_ns),
"offset_lower_ns": str(self.offset_lower_ns),
"offset_upper_ns": str(self.offset_upper_ns),
"relative_rate_budget_ppm": self.rate_ppm,
"timestamp_error_budget_ns": str(self.timestamp_error_ns),
"samples": self.samples,
"conditional_rate_error_envelope": True,
"symmetric_network_assumed": False,
}
class ClockWindow:
def __init__(
self,
local_clock_id: str,
remote_clock_id: str,
*,
rate_ppm: int,
timestamp_error_ns: int,
maximum_age_ns: int = 2_000_000_000,
) -> None:
for value in (local_clock_id, remote_clock_id):
identifier(value)
if (
type(rate_ppm) is not int
or not 1 <= rate_ppm <= 1000
or type(timestamp_error_ns) is not int
or not 1 <= timestamp_error_ns <= 1_000_000
or type(maximum_age_ns) is not int
or not 0 < maximum_age_ns <= 5_000_000_000
):
raise ClockMappingError("explicit bounded clock envelope is required")
self.local_clock_id, self.remote_clock_id = local_clock_id, remote_clock_id
self.rate_ppm, self.timestamp_error_ns, self.maximum_age_ns = (
rate_ppm,
timestamp_error_ns,
maximum_age_ns,
)
self.samples: deque[ClockProbe] = deque(maxlen=16)
self.failed = False
self.last_receive_ns = -1
def add(self, sample: ClockProbe) -> ClockBounds:
if self.failed:
raise ClockMappingError("clock session quarantined; explicit new session required")
if (
(sample.local_clock_id, sample.remote_clock_id)
!= (self.local_clock_id, self.remote_clock_id)
or sample.local_send_ns <= self.last_receive_ns
or any(old.nonce == sample.nonce for old in self.samples)
):
raise ClockMappingError("foreign, overlapping or replayed clock probe")
while (
self.samples
and sample.local_receive_ns - self.samples[0].local_receive_ns >= self.maximum_age_ns
):
self.samples.popleft()
# A new sample must intersect the still-valid old evidence. Do not
# silently forget a contradictory sample to manufacture a narrower fit.
bounds = [
old.bounds_at(
sample.local_receive_ns,
rate_ppm=self.rate_ppm,
timestamp_error_ns=self.timestamp_error_ns,
)
for old in (*self.samples, sample)
]
lower, upper = max(x[0] for x in bounds), min(x[1] for x in bounds)
if lower > upper:
self.failed = True
raise ClockMappingError("clock jump or violated rate/error envelope")
self.samples.append(sample)
self.last_receive_ns = sample.local_receive_ns
return self.current(sample.local_receive_ns)
def current(self, local_ns: int) -> ClockBounds:
_stamp(local_ns)
if self.failed or not self.samples or local_ns < self.last_receive_ns:
raise ClockMappingError("no valid monotonic clock mapping")
recent = [x for x in self.samples if local_ns - x.local_receive_ns < self.maximum_age_ns]
if not recent:
raise ClockMappingError("clock mapping expired")
bounds = [
x.bounds_at(
local_ns, rate_ppm=self.rate_ppm, timestamp_error_ns=self.timestamp_error_ns
)
for x in recent
]
lower, upper = max(x[0] for x in bounds), min(x[1] for x in bounds)
return ClockBounds(
self.local_clock_id,
self.remote_clock_id,
local_ns,
min(x.local_receive_ns for x in recent) + self.maximum_age_ns,
lower,
upper,
self.rate_ppm,
self.timestamp_error_ns,
len(recent),
)
@@ -0,0 +1,269 @@
"""Capability-scoped clock observation and pending-grant delivery over TLS.
Poll cannot acquire/renew a GPU lease, pick an epoch, reset models or cancel a
run. Only the trusted local controller supplies a pending grant. A request nonce
binds each clock response; authenticated polling is rate/buffer bounded. The
per-activation bootstrap ticket is provisioned separately, never in CLI/logs.
"""
from __future__ import annotations
import hmac
import secrets
import threading
import time
from collections.abc import Callable
from dataclasses import dataclass, field, replace
from hashlib import sha256
from typing import Any
import grpc # type: ignore[import-untyped]
from . import streaming_wire as wire
from .realtime_contract import StreamStart
from .streaming_clock import ClockProbe
from .streaming_grpc import OPTIONS, StreamAccess
SERVICE = "missioncore.perception.v1.StreamControl"
METHOD = f"/{SERVICE}/Poll"
MAX_CONTROL = 8192
def _document(raw: bytes) -> dict[str, Any]:
if type(raw) is not bytes or not 0 < len(raw) <= MAX_CONTROL:
raise wire.StreamWireError("control message exceeds bound")
return wire.parse_header(bytearray(raw))
def _token(value: object) -> None:
if (
not isinstance(value, str)
or len(value) != 64
or any(c not in "0123456789abcdef" for c in value)
):
raise ValueError("invalid capability")
def _offer(access: StreamAccess, activation: StreamStart) -> None:
if replace(access.epoch, epoch_id=activation.epoch_id) != activation:
raise ValueError("pending grant belongs to another activation")
wire.identifier(access.session_id)
wire.decimal(access.session_generation)
if access.session_generation < 1:
raise ValueError("invalid capture generation")
_token(access.token)
@dataclass(frozen=True)
class ControlTicket:
activation: StreamStart
token: str = field(repr=False)
def __post_init__(self) -> None:
_token(self.token)
def metadata(self) -> tuple[tuple[str, str], ...]:
return (
("authorization", "Bearer " + self.token),
("x-stream-binding", wire.binding(self.activation)),
)
class StreamControlEndpoint:
def __init__(
self,
ticket: ControlTicket,
pending: Callable[[], StreamAccess | None],
*,
clock_id: str,
clock_ns: Callable[[], int] = time.monotonic_ns,
) -> None:
wire.identifier(clock_id)
self.activation, self.pending, self.clock_id, self.clock_ns = (
ticket.activation,
pending,
clock_id,
clock_ns,
)
self._token_hash = sha256(ticket.token.encode()).digest()
self._lock = threading.Lock()
self._last_poll_ns = -1
self.accepted = self.rejected = 0
def handler(self) -> Any:
return grpc.method_handlers_generic_handler(
SERVICE, {"Poll": grpc.unary_unary_rpc_method_handler(self.poll)}
)
def _authenticate(self, context: Any) -> None:
if b"ssl" not in context.auth_context().get("transport_security_type", ()):
raise wire.StreamWireError("TLS control capability required")
metadata = list(context.invocation_metadata())
relevant = [(k, v) for k, v in metadata if k in ("authorization", "x-stream-binding")]
values = dict(relevant)
token = values.get("authorization")
if (
len(relevant) != 2
or len(values) != 2
or not isinstance(token, str)
or len(token) != 71
or not token.startswith("Bearer ")
or values.get("x-stream-binding") != wire.binding(self.activation)
or not hmac.compare_digest(sha256(token[7:].encode()).digest(), self._token_hash)
):
raise wire.StreamWireError("control capability rejected")
async def poll(self, raw: bytes, context: Any) -> bytes:
received = self.clock_ns()
try:
self._authenticate(context)
request = _document(raw)
if set(request) != {"nonce", "local_clock_id", "local_send_ns"}:
raise wire.StreamWireError("unsupported control request")
wire.identifier(request["nonce"])
wire.identifier(request["local_clock_id"])
if wire.uint64(request["local_send_ns"]) >= 2**63:
raise ValueError("invalid monotonic timestamp")
except (ValueError, TypeError):
self.rejected += 1
await context.abort(
grpc.StatusCode.UNAUTHENTICATED, "control capability/request rejected"
)
return b""
with self._lock:
limited = self._last_poll_ns >= 0 and received - self._last_poll_ns < 25_000_000
if not limited:
self._last_poll_ns = received
if limited:
self.rejected += 1
await context.abort(
grpc.StatusCode.RESOURCE_EXHAUSTED, "bounded control poll rate exceeded"
)
return b""
try:
offer = self.pending() # Trusted constant-time snapshot; no model/control work.
if offer is not None:
_offer(offer, self.activation)
response = {
"schema_version": "missioncore.stream-control-poll/v1",
"activation_binding": wire.binding(self.activation),
**request,
"remote_clock_id": self.clock_id,
"remote_receive_ns": str(received),
"remote_send_ns": str(self.clock_ns()),
"grant": None
if offer is None
else {
"epoch": offer.epoch.to_dict(),
"session_id": offer.session_id,
"session_generation": str(offer.session_generation),
"token": offer.token,
},
}
encoded = wire.canonical(response)
if len(encoded) > MAX_CONTROL:
raise ValueError("trusted control snapshot exceeds bound")
except (ValueError, RuntimeError):
await context.abort(
grpc.StatusCode.UNAVAILABLE, "local controller has no current offer"
)
return b""
self.accepted += 1
return encoded
class StreamControlClient:
def __init__(
self,
target: str,
roots: bytes,
ticket: ControlTicket,
*,
clock_id: str,
clock_ns: Callable[[], int] = time.monotonic_ns,
) -> None:
if not roots:
raise ValueError("trusted TLS roots required")
wire.identifier(clock_id)
self.ticket, self.clock_id, self.clock_ns = ticket, clock_id, clock_ns
self.channel = grpc.aio.secure_channel(
target,
grpc.ssl_channel_credentials(root_certificates=roots),
options=OPTIONS
+ (
("grpc.max_send_message_length", MAX_CONTROL),
("grpc.max_receive_message_length", MAX_CONTROL),
),
)
self.call = self.channel.unary_unary(METHOD)
self.polling = False
async def poll(self) -> tuple[ClockProbe, StreamAccess | None]:
if self.polling:
raise ValueError("one outstanding clock/control probe per client")
self.polling = True
nonce = secrets.token_hex(16)
sent = self.clock_ns()
try:
raw = await self.call(
wire.canonical(
{"nonce": nonce, "local_clock_id": self.clock_id, "local_send_ns": str(sent)}
),
metadata=self.ticket.metadata(),
timeout=0.5,
wait_for_ready=False,
)
received = self.clock_ns()
response = _document(raw)
if (
set(response)
!= {
"schema_version",
"activation_binding",
"nonce",
"local_clock_id",
"local_send_ns",
"remote_clock_id",
"remote_receive_ns",
"remote_send_ns",
"grant",
}
or response["schema_version"] != "missioncore.stream-control-poll/v1"
or response["activation_binding"] != wire.binding(self.ticket.activation)
or response["nonce"] != nonce
or response["local_clock_id"] != self.clock_id
or response["local_send_ns"] != str(sent)
):
raise wire.StreamWireError("clock/control response binding mismatch")
probe = ClockProbe(
self.clock_id,
response["remote_clock_id"],
nonce,
sent,
wire.uint64(response["remote_receive_ns"]),
wire.uint64(response["remote_send_ns"]),
received,
)
grant = response["grant"]
access = None
if grant is not None:
if not isinstance(grant, dict) or set(grant) != {
"epoch",
"session_id",
"session_generation",
"token",
}:
raise wire.StreamWireError("invalid control grant shape")
access = StreamAccess(
StreamStart.from_dict(grant["epoch"]),
grant["session_id"],
wire.uint64(grant["session_generation"]),
grant["token"],
)
_offer(access, self.ticket.activation)
return probe, access
finally:
self.polling = False
async def close(self) -> None:
await self.channel.close()
+14 -2
View File
@@ -149,7 +149,14 @@ class GrpcStreamEndpoint:
if self.active is not None and self.active[0] == epoch:
self.active[1].finish()
async def serve(self, address: str, *, certificate: bytes, private_key: bytes) -> Any:
async def serve(
self,
address: str,
*,
certificate: bytes,
private_key: bytes,
control_handlers: tuple[Any, ...] = (),
) -> Any:
if not certificate or not private_key:
raise ValueError("TLS identity is required")
server = grpc.aio.server(
@@ -158,7 +165,11 @@ class GrpcStreamEndpoint:
("grpc.max_receive_message_length", INPUT_CHUNK),
("grpc.max_send_message_length", MAX_REPLY),
),
maximum_concurrent_rpcs=2,
# aio reserves its next accept before awaiting a request. Leave
# one bounded accept slot beside Exchange + a completing Poll;
# otherwise the next Poll can inherit a stale "limit exceeded".
# Data ownership still admits exactly one Exchange, never 2 GPUs.
maximum_concurrent_rpcs=3 if control_handlers else 2,
compression=grpc.Compression.NoCompression,
)
server.add_generic_rpc_handlers(
@@ -166,6 +177,7 @@ class GrpcStreamEndpoint:
grpc.method_handlers_generic_handler(
SERVICE, {"Exchange": grpc.stream_stream_rpc_method_handler(self.exchange)}
),
*control_handlers,
)
)
port = server.add_secure_port(
+134
View File
@@ -0,0 +1,134 @@
"""Conditional clock intervals, never a symmetric-network offset assertion."""
from dataclasses import replace
import pytest
from k1link.perception.streaming_clock import ClockMappingError, ClockProbe, ClockWindow
BASE = 10**16 # Deliberately above IEEE754 exact-integer range.
def probe(number=1, *, outbound=1_000_000, inbound=2_000_000, offset=-(10**12)):
sent = BASE + number * 100_000_000
return ClockProbe(
"mac",
"worker",
str(number),
sent,
sent + offset + outbound,
sent + offset + outbound + 100_000,
sent + outbound + inbound + 100_000,
)
def window(**kwargs):
return ClockWindow("mac", "worker", rate_ppm=500, timestamp_error_ns=50_000, **kwargs)
@pytest.mark.parametrize("offset", [-(10**12), 0, 10**12])
@pytest.mark.parametrize("outbound,inbound", [(1, 9_000_000), (9_000_000, 1), (1, 1)])
def test_contains_true_offset_without_symmetric_path_assumption(offset, outbound, inbound):
sample = probe(offset=offset, outbound=outbound, inbound=inbound)
mapping = window().add(sample)
now = sample.local_receive_ns
assert mapping.offset_at(now)[0] <= offset <= mapping.offset_at(now)[1]
assert mapping.uncertainty_ns(now + 1_000_000) > mapping.uncertainty_ns(now)
value = mapping.to_dict()
assert value["measured_at_ns"] == str(now)
assert not value["symmetric_network_assumed"]
assert value["timestamp_error_budget_ns"] == "50000"
def test_intersection_requires_evidence_and_drift_is_not_averaged_away():
mapping = window()
first = mapping.add(probe(outbound=1_000_000, inbound=20_000_000))
with pytest.raises(ClockMappingError, match="budget"):
first.require(first.measured_at_ns)
second = mapping.add(probe(2, outbound=20_000_000, inbound=1_000_000))
second.require(second.measured_at_ns)
assert second.uncertainty_ns(second.measured_at_ns) < 2_000_000
assert (
second.offset_at(second.measured_at_ns)[0]
< -(10**12)
< second.offset_at(second.measured_at_ns)[1]
)
def test_expired_mapping_waits_and_new_observation_can_restore_it():
mapping = window()
first = mapping.add(probe())
with pytest.raises(ClockMappingError, match="expired"):
first.require(first.expires_at_ns)
with pytest.raises(ClockMappingError, match="expired"):
mapping.current(first.expires_at_ns)
resumed = mapping.add(probe(30))
assert resumed.samples == 1
resumed.require(resumed.measured_at_ns)
def test_contradictory_samples_quarantine_not_cherry_pick():
mapping = window()
mapping.add(probe())
with pytest.raises(ClockMappingError, match="envelope"):
mapping.add(probe(2, offset=0))
with pytest.raises(ClockMappingError, match="quarantined"):
mapping.add(probe(3))
with pytest.raises(ClockMappingError):
mapping.current(BASE + 3_000_000_000)
@pytest.mark.parametrize("fault", ["local", "remote", "nonce", "overlap"])
def test_foreign_or_replayed_observation_cannot_refresh_mapping(fault):
mapping = window()
original = probe()
mapping.add(original)
second = probe(2)
changes = {
"local": {"local_clock_id": "other"},
"remote": {"remote_clock_id": "rebooted"},
"nonce": {"nonce": original.nonce},
"overlap": {"local_send_ns": original.local_receive_ns},
}
with pytest.raises(ClockMappingError):
mapping.add(replace(second, **changes[fault]))
assert mapping.last_receive_ns == original.local_receive_ns
def test_history_is_bounded_and_exact_values_survive():
mapping = window(maximum_age_ns=5_000_000_000)
for number in range(1, 35):
bounds = mapping.add(probe(number))
assert len(mapping.samples) == bounds.samples == 16
assert bounds.to_dict()["measured_at_ns"] == str(probe(34).local_receive_ns)
@pytest.mark.parametrize("value", [-1, 2**63, True, 1.0])
def test_invalid_monotonic_timestamp_rejected(value):
with pytest.raises(ClockMappingError):
replace(probe(), local_send_ns=value)
def test_deadline_remote_order_and_backward_reads():
sample = probe()
for changes in (
{"local_receive_ns": sample.local_send_ns - 1},
{"local_receive_ns": sample.local_send_ns + 2_000_000_001},
{"remote_send_ns": sample.remote_receive_ns - 1},
):
with pytest.raises(ClockMappingError):
replace(sample, **changes)
mapping = window().add(sample)
with pytest.raises(ClockMappingError):
mapping.offset_at(mapping.measured_at_ns - 1)
with pytest.raises(ClockMappingError):
replace(mapping, offset_lower_ns=mapping.offset_upper_ns + 1)
@pytest.mark.parametrize(
"rate,error,age",
[(0, 1, 1), (1001, 1, 1), (True, 1, 1), (1, 0, 1), (1, 1_000_001, 1), (1, 1, 0)],
)
def test_controller_must_supply_bounded_envelope(rate, error, age):
with pytest.raises(ClockMappingError):
ClockWindow("mac", "worker", rate_ppm=rate, timestamp_error_ns=error, maximum_age_ns=age)
@@ -0,0 +1,206 @@
"""Small TLS clock/grant checks; no model workload or lease authority in Poll."""
# ruff: noqa: E402, F811 -- imported shared pytest TLS fixture.
import asyncio
import threading
import time
from contextlib import asynccontextmanager
from dataclasses import replace
from hashlib import sha256
import pytest
grpc = pytest.importorskip("grpc")
from test_perception_streaming_grpc import event, eventually, identity, tls # noqa: F401
from k1link.perception import streaming_wire as wire
from k1link.perception.streaming_clock import ClockWindow
from k1link.perception.streaming_continuity import ResumeEvidence
from k1link.perception.streaming_control_grpc import (
MAX_CONTROL,
ControlTicket,
StreamControlClient,
StreamControlEndpoint,
_offer,
)
from k1link.perception.streaming_grpc import GrpcStreamClient, GrpcStreamEndpoint, StreamAccess
from k1link.perception.streaming_lifecycle import StreamingLifecycle
from k1link.perception.streaming_queue import StreamMailbox
@asynccontextmanager
async def controlled(tmp_path, tls):
run = StreamingLifecycle(
identity(),
tmp_path,
StreamMailbox(),
threading.Event(),
recover_input=True,
source_clock_ns=lambda: 1_000_000_000,
)
run.ready()
endpoint = GrpcStreamEndpoint(
run,
lambda value: endpoint.publish(
run.continuity.epoch, value.ingress_sequence, sha256(value.payload).digest()
),
lambda _: None,
)
pending = [endpoint.issue(identity(), "capture", 1)]
ticket = ControlTicket(identity(), "b" * 64)
control = StreamControlEndpoint(ticket, lambda: pending[0], clock_id="worker")
server, port = await endpoint.serve(
"localhost:0",
certificate=tls[0],
private_key=tls[1],
control_handlers=(control.handler(),),
)
client = StreamControlClient(f"localhost:{port}", tls[0], ticket, clock_id="mac")
try:
yield run, endpoint, control, client, pending, port
finally:
await client.close()
await server.stop(0)
await eventually(lambda: endpoint.active is None)
assert run.close()
def test_control_delivery_coexists_with_stream_without_lease_or_model_authority(tmp_path, tls):
async def check():
async with controlled(tmp_path, tls) as (run, endpoint, control, client, pending, port):
probe, access = await client.poll()
assert access == pending[0]
mapping = ClockWindow("mac", "worker", rate_ppm=500, timestamp_error_ns=50_000)
assert mapping.add(probe).uncertainty_ns(probe.local_receive_ns) > 0
first = access
for phase in range(2):
stream = GrpcStreamClient(f"localhost:{port}", tls[0], access)
try:
await stream.open()
pending[0] = None
await stream.send(event(phase + 1))
assert await stream.receive() == (phase + 1, sha256(event().payload).digest())
await asyncio.sleep(0.03)
_, absent = await client.poll()
assert absent is None and endpoint.active is not None
finally:
await stream.close()
await eventually(lambda: endpoint.active is None)
assert run.continuity.phase == "waiting"
assert run.lease.start == identity()
if phase == 0:
run.renew(identity()) # Only independent trusted owner renews lease.
epoch = run.begin_input(identity())
run.resume_input(
epoch, ResumeEvidence(*([1_000_000_000] * 4), True), lambda: None
)
pending[0] = endpoint.issue(epoch, "capture", 1)
await asyncio.sleep(0.03)
_, access = await client.poll()
assert access.epoch != first.epoch and access.token != first.token
assert control.accepted == 4 and run.lease.start.lease_generation == 1
asyncio.run(check())
@pytest.mark.parametrize("fault", ["secret", "binding", "duplicate", "oversize", "int64", "extra"])
def test_bad_control_cannot_claim_data_or_change_owner(tmp_path, tls, fault):
async def check():
async with controlled(tmp_path, tls) as (run, endpoint, control, client, pending, _):
metadata = client.ticket.metadata()
request = {"nonce": "n", "local_clock_id": "mac", "local_send_ns": "1"}
if fault == "secret":
metadata = ControlTicket(identity(), "0" * 64).metadata()
elif fault == "binding":
metadata = ControlTicket(
replace(identity(), source_id="other"), "b" * 64
).metadata()
elif fault == "duplicate":
metadata = (*metadata, *metadata)
elif fault == "int64":
request["local_send_ns"] = str(2**63)
elif fault == "extra":
request["renew_lease"] = True
raw = b"x" * (MAX_CONTROL + 1) if fault == "oversize" else wire.canonical(request)
with pytest.raises(grpc.aio.AioRpcError):
await client.call(raw, metadata=metadata, timeout=1)
assert endpoint.active is None and endpoint.grant is not None
assert run.lease.start == identity() and run.mailbox.bytes == 0
assert control.accepted == 0
asyncio.run(check())
def test_control_rate_and_single_outstanding_are_bounded(tmp_path, tls):
async def check():
async with controlled(tmp_path, tls) as (_, _, control, client, _, _):
control.clock_ns = lambda: 10**16
await client.poll()
with pytest.raises(grpc.aio.AioRpcError) as exc:
await client.poll()
assert exc.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED
client.polling = True
with pytest.raises(ValueError, match="outstanding"):
await client.poll()
assert control.accepted == control.rejected == 1
asyncio.run(check())
@pytest.mark.parametrize(
"changes",
[
{"source_id": "other"},
{"clock_domain_id": "other"},
{"input_mode": "recorded-source-paced"},
{"lease_generation": 2},
],
)
def test_offer_is_bound_to_every_activation_field(changes):
access = StreamAccess(replace(identity(), **changes), "capture", 1, "a" * 64)
with pytest.raises(ValueError):
_offer(access, identity())
@pytest.mark.parametrize("value", [None, 1, "x" * 64, "a" * 63])
def test_invalid_capabilities_fail_closed_without_leaking_in_repr(value):
with pytest.raises(ValueError):
ControlTicket(identity(), value)
assert "b" * 64 not in repr(ControlTicket(identity(), "b" * 64))
def test_malicious_response_nonce_or_offer_rejected(tls):
async def check():
ticket = ControlTicket(identity(), "a" * 64)
client = StreamControlClient("localhost:1", tls[0], ticket, clock_id="mac")
for mode in ("nonce", "offer"):
async def fake(raw, mode=mode, **kwargs):
request = wire.parse_header(bytearray(raw))
return wire.canonical(
{
"schema_version": "missioncore.stream-control-poll/v1",
"activation_binding": wire.binding(identity()),
**request,
"nonce": "wrong" if mode == "nonce" else request["nonce"],
"remote_clock_id": "worker",
"remote_receive_ns": str(time.monotonic_ns()),
"remote_send_ns": str(time.monotonic_ns()),
"grant": {
"epoch": replace(identity(), source_id="foreign").to_dict(),
"session_id": "capture",
"session_generation": "1",
"token": "a" * 64,
},
}
)
client.call = fake
with pytest.raises(ValueError):
await client.poll()
assert not client.polling
await client.close()
asyncio.run(check())