Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2026 veyron-core
Copyright (c) 2026 vynkor-core

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,10 @@ Other client methods: `recv()` / `recv_frame()` / `recv_timeout(timeout)`,

All SDK-level failures raise `vynkor.VynkorError` (or a subclass) instead of
bare `ValueError` / `RuntimeError` / `TimeoutError`. Subclasses mirror the
Rust `WireError` variants: `VeyronIoError`, `VeyronProtoError`,
`VeyronFrameMagicMismatch`, `VeyronFrameCrcMismatch`, `VeyronFrameReadTimeout`,
`VeyronPayloadTooLarge`, `VeyronTimeout`, `VeyronPermissionDenied`,
`VeyronInternal`.
Rust `WireError` variants: `VynkorIoError`, `VynkorProtoError`,
`VynkorFrameMagicMismatch`, `VynkorFrameCrcMismatch`, `VynkorFrameReadTimeout`,
`VynkorPayloadTooLarge`, `VynkorTimeout`, `VynkorPermissionDenied`,
`VynkorInternal`.

## Development

Expand Down
2 changes: 1 addition & 1 deletion examples/echo_plugin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Lightweight demo plugin for the Veyron Python SDK.
"""Lightweight demo plugin for the Vynkor Python SDK.

Shows: lifecycle hooks, manifest declaration, action handling (plain,
streaming, and publish-from-plugin), event subscription, and SessionClose
Expand Down
6 changes: 3 additions & 3 deletions fuzz/fuzz_framing.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@

with atheris.instrument_imports():
from vynkor.framing import read_frame
from vynkor.errors import VeyronError
from vynkor.errors import VynkorError

FIXED_KEY = bytes(range(32))


def test_one_input(data: bytes) -> None:
try:
read_frame(io.BytesIO(data))
except VeyronError:
except VynkorError:
pass # rejecting malformed input is expected, correct behavior

try:
read_frame(io.BytesIO(data), session_key=FIXED_KEY)
except VeyronError:
except VynkorError:
pass


Expand Down
6 changes: 3 additions & 3 deletions tests/test_framing_compressed.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
pack_frame,
read_frame,
)
from vynkor.errors import VeyronInternal
from vynkor.errors import VynkorInternal


def _pack_compressed_frame(target: str, plain_payload: bytes, session_key=None) -> bytes:
Expand Down Expand Up @@ -67,7 +67,7 @@ def test_read_frame_rejects_bad_mac_on_compressed_frame():
wrong_key = derive_session_key(b"different", b"nonce123", "plugin-a")
payload = b"y" * 100_000
frame = _pack_compressed_frame("kernel", payload, session_key=session_key)
with pytest.raises(VeyronInternal, match="MAC"):
with pytest.raises(VynkorInternal, match="MAC"):
read_frame(io.BytesIO(frame), session_key=wrong_key)


Expand All @@ -85,5 +85,5 @@ def test_read_frame_rejects_garbage_compressed_payload():
target_bytes = b"kernel".ljust(32, b"\x00")[:32]
crc = crc32(garbage) & 0xFFFFFFFF
header = struct.pack(HEADER_FMT, MAGIC, FLAG_COMPRESSED, len(garbage), target_bytes, crc)
with pytest.raises(VeyronInternal):
with pytest.raises(VynkorInternal):
read_frame(io.BytesIO(header + garbage))
4 changes: 2 additions & 2 deletions tests/test_framing_mac.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
async_read_frame,
HEADER_SIZE,
)
from vynkor.errors import VeyronInternal
from vynkor.errors import VynkorInternal


def test_derive_session_key_is_deterministic():
Expand Down Expand Up @@ -97,7 +97,7 @@ def test_read_frame_mac_rejects_tampered():
payload = b"round trip"
frame = bytearray(pack_frame("tgt", payload, session_key=key))
frame[-1] ^= 0xFF # corrupt last byte of MAC tag
with pytest.raises(VeyronInternal, match="MAC verification failed"):
with pytest.raises(VynkorInternal, match="MAC verification failed"):
read_frame(io.BytesIO(bytes(frame)), session_key=key)


Expand Down
6 changes: 3 additions & 3 deletions tests/test_publish_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import pytest

from vynkor import VynkorClient
from vynkor.errors import VeyronInternal, VeyronTimeout
from vynkor.errors import VynkorInternal, VynkorTimeout
from vynkor.framing import pack_frame, read_frame
from vynkor.vynkor_protocol_pb2 import (
Envelope,
Expand Down Expand Up @@ -94,7 +94,7 @@ def kernel():

t = threading.Thread(target=kernel)
t.start()
with pytest.raises(VeyronInternal):
with pytest.raises(VynkorInternal):
await client.publish_event("my.event", b"{}", 1000)
t.join()

Expand All @@ -110,7 +110,7 @@ def kernel():
t = threading.Thread(target=kernel)
t.start()
start = time.monotonic()
with pytest.raises(VeyronTimeout):
with pytest.raises(VynkorTimeout):
await client.publish_event("my.event", b"{}", 150)
elapsed = time.monotonic() - start
assert elapsed < 2.0
Expand Down
2 changes: 1 addition & 1 deletion tests/test_sdk.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Integration tests for the Python Veyron SDK.
"""Integration tests for the Python Vynkor SDK.

Requires a running kernel (vyn start --foreground) at /tmp/vyn.sock.
Run with: pytest tests/ -v
Expand Down
8 changes: 4 additions & 4 deletions tests/test_send_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import pytest

from vynkor import VynkorClient
from vynkor.errors import VeyronInternal, VeyronTimeout
from vynkor.errors import VynkorInternal, VynkorTimeout
from vynkor.framing import pack_frame, read_frame
from vynkor.vynkor_protocol_pb2 import (
Envelope,
Expand Down Expand Up @@ -76,7 +76,7 @@ def kernel():

t = threading.Thread(target=kernel)
t.start()
with pytest.raises(VeyronInternal):
with pytest.raises(VynkorInternal):
await client.send_action("get_weather", b"{}", 1000)
t.join()

Expand All @@ -94,7 +94,7 @@ def kernel():

t = threading.Thread(target=kernel)
t.start()
with pytest.raises(VeyronInternal):
with pytest.raises(VynkorInternal):
await client.send_action("get_weather", b"{}", 1000)
t.join()

Expand All @@ -110,7 +110,7 @@ def kernel():
t = threading.Thread(target=kernel)
t.start()
start = time.monotonic()
with pytest.raises(VeyronTimeout):
with pytest.raises(VynkorTimeout):
await client.send_action("get_weather", b"{}", 150)
elapsed = time.monotonic() - start
assert elapsed < 2.0
Expand Down
64 changes: 32 additions & 32 deletions vynkor/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
from typing import Callable, Optional

from .errors import (
VeyronInternal,
VeyronPayloadTooLarge,
VeyronProtoError,
VeyronTimeout,
VynkorInternal,
VynkorPayloadTooLarge,
VynkorProtoError,
VynkorTimeout,
)
from .framing import (
FLAG_FRAGMENTED,
Expand Down Expand Up @@ -194,7 +194,7 @@ async def connect_ws(
import websockets
import websockets.exceptions
except ImportError as e:
raise VeyronInternal(
raise VynkorInternal(
"websockets package required for WebSocket transport: pip install vynkor-sdk[websockets] or websockets>=12"
) from e

Expand Down Expand Up @@ -280,10 +280,10 @@ async def register_full(
self._apply_session_nonce(plugin_id, nonce)
return ack
if response.HasField("error"):
raise VeyronInternal(
raise VynkorInternal(
f"registration rejected: {response.error.message} ({response.error.details})"
)
raise VeyronInternal("expected PluginRegisterAck")
raise VynkorInternal("expected PluginRegisterAck")

# ── Sending ─────────────────────────────────────────────────────

Expand All @@ -304,15 +304,15 @@ async def send_raw_with_flags(self, target: str, extra_flags: int, payload: byte
"""
if self._is_ws():
if len(payload) > MAX_PAYLOAD:
raise VeyronPayloadTooLarge(len(payload))
raise VynkorPayloadTooLarge(len(payload))
# Never compress over WS; MAC still applies
frame = pack_frame(
target, payload, flags=extra_flags, session_key=self.session_key, compress=False
)
try:
await self._ws.send(frame) # type: ignore[union-attr]
except Exception as e:
raise VeyronInternal(f"websocket send failed: {e}") from e
raise VynkorInternal(f"websocket send failed: {e}") from e
else:
frame = pack_frame(target, payload, flags=extra_flags, session_key=self.session_key)
assert self._writer is not None, "not connected"
Expand All @@ -329,14 +329,14 @@ async def send_fragmented(self, target: str, payload: bytes, chunk_size: int) ->
so this raises on a WebSocket transport.
"""
if self._is_ws():
raise VeyronInternal("fragmented frames are not supported over WebSocket (R5-03)")
raise VynkorInternal("fragmented frames are not supported over WebSocket (R5-03)")
if len(payload) > MAX_PAYLOAD:
raise VeyronPayloadTooLarge(len(payload))
raise VynkorPayloadTooLarge(len(payload))
if chunk_size <= 0 or chunk_size + FRAG_HEADER_SIZE > MAX_PAYLOAD:
raise VeyronInternal(f"invalid fragment chunk_size: {chunk_size}")
raise VynkorInternal(f"invalid fragment chunk_size: {chunk_size}")
total = max(1, -(-len(payload) // chunk_size)) # ceil div
if total > 0xFFFF:
raise VeyronInternal(f"payload needs {total} fragments; max is 65535")
raise VynkorInternal(f"payload needs {total} fragments; max is 65535")

stream_id = self._next_stream_id
self._next_stream_id = (self._next_stream_id + 1) & 0xFFFFFFFF or 1
Expand Down Expand Up @@ -377,7 +377,7 @@ async def _ws_recv_frame(self):
except Exception as e:
# Map websocket close/error to Io-like error so callers see
# disconnect / EOF, matching UDS behavior
raise VeyronInternal(f"websocket connection closed: {e}") from e
raise VynkorInternal(f"websocket connection closed: {e}") from e
# websockets may deliver str for text frames — ignore them (kernel
# gateway never sends them as traffic, per Rust docs)
if isinstance(data, str):
Expand All @@ -389,21 +389,21 @@ async def _ws_recv_frame(self):
async def recv(self) -> Envelope:
flags, payload = await self.recv_frame()
if flags & FLAG_RAW_BINARY:
raise VeyronInternal("received raw-binary frame; use recv_frame() for audio")
raise VynkorInternal("received raw-binary frame; use recv_frame() for audio")
env = Envelope()
try:
env.ParseFromString(payload)
except Exception as e:
raise VeyronProtoError(str(e)) from e
raise VynkorProtoError(str(e)) from e
return env

async def recv_timeout(self, timeout: float) -> Envelope:
"""Receive and decode the next Envelope, bounded by ``timeout`` seconds.
Raises VeyronTimeout if nothing arrives in time."""
Raises VynkorTimeout if nothing arrives in time."""
try:
return await asyncio.wait_for(self.recv(), timeout=timeout)
except asyncio.TimeoutError:
raise VeyronTimeout() from None
raise VynkorTimeout() from None

def _prune_reassembly(self) -> None:
"""Stale sets can't pin memory forever."""
Expand All @@ -421,18 +421,18 @@ def _absorb_fragment(self, flags: int, payload: bytes):
complete, else ``None``. Mirrors the Rust SDK's ``absorb_fragment``."""
hdr = parse_frag_header(payload)
if hdr is None:
raise VeyronInternal("fragment header too short")
raise VynkorInternal("fragment header too short")
_fragment_id, seq, total, stream_id = hdr
if total == 0 or seq >= total:
raise VeyronInternal(f"invalid fragment header: seq {seq} / total {total}")
raise VynkorInternal(f"invalid fragment header: seq {seq} / total {total}")

buf = self._reassembly.get(stream_id)
if buf is not None:
if buf.total != total:
del self._reassembly[stream_id]
raise VeyronInternal("fragment total mismatch within stream")
raise VynkorInternal("fragment total mismatch within stream")
elif len(self._reassembly) >= MAX_REASSEMBLY_STREAMS:
raise VeyronInternal("too many concurrent fragment streams")
raise VynkorInternal("too many concurrent fragment streams")
else:
buf = _ReassemblyBuf(total, flags & ~(FLAG_FRAGMENTED | FLAG_MAC_PRESENT))
self._reassembly[stream_id] = buf
Expand All @@ -442,7 +442,7 @@ def _absorb_fragment(self, flags: int, payload: bytes):
new_total = buf.buffered_bytes - replaced_len + len(chunk)
if new_total > MAX_PAYLOAD:
del self._reassembly[stream_id]
raise VeyronPayloadTooLarge(MAX_PAYLOAD + 1)
raise VynkorPayloadTooLarge(MAX_PAYLOAD + 1)
buf.buffered_bytes = new_total
buf.fragments[seq] = chunk

Expand Down Expand Up @@ -478,7 +478,7 @@ async def publish_event(
) -> EventPublishAck:
"""Publish an event to the kernel event bus. Requires
``PERMISSION_EVENT_PUBLISH``. ``timeout_ms == 0`` uses the kernel default of
30s. Raises ``VeyronInternal`` on a kernel Error envelope, ``VeyronTimeout`` on
30s. Raises ``VynkorInternal`` on a kernel Error envelope, ``VynkorTimeout`` on
deadline expiry. The returned ``EventPublishAck`` is returned as-is
regardless of its status field — callers inspect ``ack.status``
themselves, mirroring the Rust SDK."""
Expand All @@ -494,7 +494,7 @@ async def publish_event(
lambda r: r.HasField("event_publish_ack") or r.HasField("error"),
)
if resp.HasField("error"):
raise VeyronInternal(
raise VynkorInternal(
f"kernel error: {resp.error.message} ({resp.error.details})"
)
return resp.event_publish_ack
Expand All @@ -504,8 +504,8 @@ async def send_action(
) -> ActionResponse:
"""Ask the kernel to perform an action and await its ``ActionResponse``.
``timeout_ms == 0`` uses the kernel default of 30s. Raises
``VeyronInternal`` on a kernel Error envelope or an ActionStreamAbort for
this ``action_id``, ``VeyronTimeout`` on deadline expiry."""
``VynkorInternal`` on a kernel Error envelope or an ActionStreamAbort for
this ``action_id``, ``VynkorTimeout`` on deadline expiry."""
action_id = _next_action_id()
env = Envelope()
env.action_request.CopyFrom(ActionRequest(
Expand All @@ -527,11 +527,11 @@ async def send_action(
or r.HasField("error"),
)
if resp.HasField("error"):
raise VeyronInternal(
raise VynkorInternal(
f"kernel error: {resp.error.message} ({resp.error.details})"
)
if resp.HasField("action_stream_abort"):
raise VeyronInternal(f"stream aborted: {resp.action_stream_abort.reason}")
raise VynkorInternal(f"stream aborted: {resp.action_stream_abort.reason}")
return resp.action_response

async def send_action_streaming(self, action: str, timeout_ms: int = 0) -> str:
Expand Down Expand Up @@ -585,7 +585,7 @@ async def send_command(
response = await self.recv()
if response.HasField("kernel_command_ack"):
return response.kernel_command_ack
raise VeyronInternal("expected KernelCommandAck")
raise VynkorInternal("expected KernelCommandAck")

async def ping(self) -> float:
"""Round-trip a ``Ping`` to the kernel; returns measured latency in
Expand All @@ -598,7 +598,7 @@ async def ping(self) -> float:
await self.send("kernel", env)
response = await self.recv()
if not response.HasField("pong"):
raise VeyronInternal("expected Pong")
raise VynkorInternal("expected Pong")
return time.monotonic() - t0

# ── Audio ───────────────────────────────────────────────────────
Expand Down Expand Up @@ -626,7 +626,7 @@ async def _await_matching(
while True:
remaining = deadline - time.monotonic()
if remaining <= 0:
raise VeyronTimeout()
raise VynkorTimeout()
resp = await self.recv_timeout(remaining)
if predicate(resp):
return resp
Expand Down
6 changes: 3 additions & 3 deletions vynkor/concurrent.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,15 @@ async def serve_concurrent(client, jwt_token: str, handler: ConcurrentHandler) -

Wraps :func:`run_concurrent_loop` with registration; ``jwt_token`` is
presented at registration (empty string on unsecured kernels). A
rejected registration raises :class:`vynkor.errors.VeyronPermissionDenied`.
rejected registration raises :class:`vynkor.errors.VynkorPermissionDenied`.
"""
from .errors import VeyronPermissionDenied
from .errors import VynkorPermissionDenied

ack = await client.register_full(
handler.id(), handler.version(), handler.manifest(), jwt_token
)
if not ack.accepted:
raise VeyronPermissionDenied(f"registration rejected: {ack.reject_reason}")
raise VynkorPermissionDenied(f"registration rejected: {ack.reject_reason}")
try:
await handler.on_init(client)
except BaseException:
Expand Down
Loading
Loading