Skip to main content

High-performance, schema-agnostic event bus for AI runtime workloads

Project description

Net Python

High-performance, schema-agnostic event bus for AI runtime workloads.

Installation

pip install net-mesh

The package publishes as net-mesh on PyPI but imports as from net import ... (the in-source module name is preserved). For the higher-level Pythonic surface (generators, typed channels, dataclass/Pydantic support), install net-mesh-sdk instead — it depends on this package.

Cargo features

The PyO3 binding wraps a feature-gated Rust crate. Wheels published to PyPI ship with every feature enabled, but anyone building from source with maturin develop / maturin build needs to ask for the features they want — symbols from a disabled feature are silently absent from net._net and the matching from net import ... block in net/__init__.py quietly skips them.

Cargo feature Surface (Python classes) Notes
cortex Redex, RedexFile, RedexEvent, TasksAdapter, MemoriesAdapter, Task, Memory, NetDb, CortexError, RedexError, NetDbError Required by meshos and dataforts.
meshdb MeshQuery, MeshQueryRunner, QueryBuilder, Predicate, ResultRow, AggregateResult, JoinedRow, LineageEntry, CachePolicy, ExecuteOptions, GroupKey, WindowBoundary, InMemoryChainReader, MeshDbError Independent.
meshos MeshOsDaemonSdk, MeshOsDaemonHandle, MeshOsSdkError Pulls in compute + cortex.
dataforts BlobRef, BlobError, MeshBlobAdapter, blob_publish, blob_resolve, adapter registry Pulls in cortex + net.
compute DaemonRuntime, DaemonHandle, CausalEvent, DaemonError, MigrationHandle, MigrationError Required by meshos and groups.
groups ReplicaGroup, ForkGroup, StandbyGroup, GroupError Pulls in compute.
deck DeckClient, OperatorIdentity, AdminCommands, SnapshotStream, StatusSummaryStream, IceCommands, OperatorRegistry, AdminVerifier, DeckSdkError Pulls in meshos.
net NetMesh, NetStream, NetKeypair, Identity, token / channel-auth helpers Encrypted mesh transport + per-peer streams.
redis RedisStreamDedup Redis Streams adapter dedup helper.
jetstream (no Python surface yet) NATS JetStream adapter — Rust-only.

All are enabled by default in Cargo.toml. Disable with maturin develop --no-default-features --features "<list>" if you need a thinner wheel.

The net.__init__ module try-imports each family conditionally, so a wheel built without (say) meshos won't surface MeshOsDaemonSdk and the corresponding from net import MeshOsDaemonSdk raises ImportError cleanly.

Quick Start

from net import Net

# Create event bus (defaults to CPU core count shards)
bus = Net()

# Ingest events - fast path with raw JSON strings (23M+ ops/sec)
bus.ingest_raw('{"token": "hello", "index": 0}')

# Or use dict for convenience (4M+ ops/sec)
bus.ingest({"token": "world", "index": 1})

# Batch ingestion for maximum throughput
events = [f'{{"token": "tok_{i}"}}' for i in range(10000)]
count = bus.ingest_raw_batch(events)

# Poll events
response = bus.poll(limit=100)
for event in response:
    print(event.raw)
    # Or parse to dict
    data = event.parse()

# Check stats
stats = bus.stats()
print(f"Ingested: {stats.events_ingested}, Dropped: {stats.events_dropped}")

# Shutdown
bus.shutdown()

Context Manager

with Net(num_shards=4) as bus:
    bus.ingest_raw('{"data": "value"}')
# Automatically shuts down

Configuration

bus = Net(
    num_shards=8,                    # Number of parallel shards
    ring_buffer_capacity=1_048_576,  # Events per shard (must be power of 2)
    backpressure_mode="drop_oldest", # What to do when full
)

Async API

Every I/O surface ships a parallel Async* class. The sync API stays unchanged — pick whichever shape fits the caller; both share the same underlying mesh / adapter / handle and interoperate freely.

Quickstart

import asyncio
from net import NetMesh, AsyncNetMesh, AsyncMeshRpc

async def main():
    mesh = NetMesh("127.0.0.1:9000", "00" * 32)
    mesh.start()

    amesh = AsyncNetMesh(mesh)            # Arc::clone — shares the same MeshNode
    arpc = AsyncMeshRpc(mesh)             # same — shared service registry

    await amesh.connect("127.0.0.1:9001", peer_pub, peer_node_id)
    reply = await arpc.call(peer_node_id, "echo", b"hello")
    print(reply)

asyncio.run(main())

Available Async* classes

Sync class Async sibling What's awaitable
NetMesh AsyncNetMesh connect, accept, push_to, poll, subscribe_channel, unsubscribe_channel, publish, announce_capabilities, shutdown
NetStream AsyncNetStream send, send_with_retry, send_blocking
MeshRpc AsyncMeshRpc call, call_service, call_streaming, call_client_stream, call_duplex, serve / serve_client_stream / serve_duplex (each accepts async def handlers)
RpcStream AsyncRpcStream PEP 525 async for chunk in stream:
ClientStreamCall AsyncClientStreamCall send, finish
DuplexCall / DuplexSink / DuplexStream AsyncDuplexCall / AsyncDuplexSink / AsyncDuplexStream send, finish_sending, async-iter on the read half
TypedMeshRpc AsyncTypedMeshRpc Codec-wrapped variants of every AsyncMeshRpc shape
MemoriesAdapter AsyncMemoriesAdapter wait_for_seq, wait_for_token, watch_memories, snapshot_and_watch_memories
MemoryWatchIter AsyncMemoryWatchIter PEP 525 async iter
TasksAdapter AsyncTasksAdapter wait_for_seq, wait_for_token, watch_tasks, snapshot_and_watch_tasks
TaskWatchIter AsyncTaskWatchIter PEP 525 async iter
RedexFile AsyncRedexFile tail() returns an AsyncRedexTailIter
RedexTailIter AsyncRedexTailIter PEP 525 async iter
DaemonRuntime AsyncDaemonRuntime start, shutdown, spawn, spawn_from_snapshot, stop, snapshot, deliver, start_migration, start_migration_with
MigrationHandle AsyncMigrationHandle wait, wait_with_timeout, cancel
MeshBlobAdapter AsyncMeshBlobAdapter store, fetch, fetch_range, exists, repair_blob
blob_publish / blob_resolve (free fns) async_blob_publish / async_blob_resolve Awaitable versions of the free functions
RegistryClient AsyncRegistryClient list, spawn, unregister
FoldQueryClient AsyncFoldQueryClient query_latest, query_summarize_now
MeshQueryRunner AsyncMeshQueryRunner execute
DeckClient AsyncDeckClient close; getters stay sync; .admin is AsyncAdminCommands; .ice is AsyncIceCommands; .snapshots() / .status_summary_stream() return async iters
AdminCommands AsyncAdminCommands drain, enter_maintenance, exit_maintenance, cordon, uncordon, drop_replicas, invalidate_placement, restart_all_daemons, clear_avoid_list
IceCommands / IceProposal / SimulatedIceProposal AsyncIceCommands / AsyncIceProposal / AsyncSimulatedIceProposal Factories stay sync; proposal.simulate() and simulated.commit() are awaitable
SnapshotStream / StatusSummaryStream AsyncSnapshotStream / AsyncStatusSummaryStream PEP 525 async iter
MeshOsDaemonSdk / MeshOsDaemonHandle AsyncMeshOsDaemonSdk / AsyncMeshOsDaemonHandle shutdown / next_control / graceful_shutdown are awaitable; constructed via from_sync(sync_inst) (consumes the sync sibling)

Mixing sync and async

Async* classes wrap the same Rust Arc<...> as their sync counterparts. A server registered via MeshRpc.serve(...) is callable from AsyncMeshRpc.call(...) and vice versa — same for every adapter. Mix shapes within a single program freely; just pick the one that matches the caller (sync script: sync API; asyncio handler: async API).

from net import NetMesh, MeshRpc, AsyncMeshRpc, MemoriesAdapter, AsyncMemoriesAdapter

mesh = NetMesh("127.0.0.1:9000", "00" * 32)
# A server, registered from sync code
def echo(req: bytes) -> bytes:
    return req
MeshRpc(mesh).serve("echo", echo)

# An async caller against the same mesh — sees the same server
async def caller():
    arpc = AsyncMeshRpc(mesh)
    return await arpc.call_service("echo", b"hi")

Cancellation

asyncio.wait_for(...) cancellation propagates to the substrate's cancel registry on every nRPC call. The wrapped task drops, the substrate's Mesh::cancel(token) fires, and the in-flight call returns RpcCancelledError:

import asyncio
from net import AsyncMeshRpc, RpcCancelledError

async def with_timeout(arpc):
    try:
        return await asyncio.wait_for(
            arpc.call(target, "slow", b"..."),
            timeout=0.5,
        )
    except (asyncio.TimeoutError, RpcCancelledError):
        # Either is fine — the underlying call is cancelled either way.
        return None

Streaming shapes propagate cancellation per construction — cancelling mid-async for terminates the entire stream, not just one pull. Non-mesh subsystems (channel publish, capability announce, mesh handshake) don't have substrate cancel tokens yet; asyncio cancellation still drops the awaiting tokio task, but the in-flight network round-trip may complete in the background.

async def server handlers

AsyncMeshRpc.serve (and the typed AsyncTypedMeshRpc.serve) detect inspect.iscoroutinefunction(handler) at register time and route the coroutine through a dedicated dispatch path. Sync handlers keep the spawn_blocking shape; async handlers run as native coroutines:

async def handler(req: bytes) -> bytes:
    chunk = await fetch_from_upstream(req)
    return chunk

AsyncMeshRpc(mesh).serve("upstream", handler)

Cancellation propagates end-to-end. When a caller's asyncio.wait_for(rpc.call(...), timeout=...) expires, the substrate fires a CANCEL frame to the server; the dispatched coroutine sees asyncio.CancelledError raised inside its await. Wrap critical sections in try/except asyncio.CancelledError if you need cleanup before re-raising.

Single dispatcher loop — keep handlers cooperative. All async def handlers in the process share one event loop. A handler that does blocking Python I/O (time.sleep, requests.get, raw socket.recv, etc.) stalls every other in-flight handler. Always await async equivalents:

# ✗ Bad — blocks the dispatcher loop for 5 seconds.
async def handler(req: bytes) -> bytes:
    time.sleep(5.0)
    return req

# ✓ Good — yields back to the loop.
async def handler(req: bytes) -> bytes:
    await asyncio.sleep(5.0)
    return req

# ✓ Good — push blocking work to a thread-pool slot.
async def handler(req: bytes) -> bytes:
    loop = asyncio.get_running_loop()
    return await loop.run_in_executor(None, blocking_call, req)

Migration cookbook

The mechanical translation:

# Before — sync
def listener(mesh):
    rpc = MeshRpc(mesh)
    reply = rpc.call(target, "svc", b"...")
    return reply

# After — async
async def listener(mesh):
    rpc = AsyncMeshRpc(mesh)
    reply = await rpc.call(target, "svc", b"...")
    return reply

For loops over streams:

# Before — sync
for chunk in rpc.call_streaming(...):
    consume(chunk)

# After — async
async for chunk in arpc.call_streaming(...):
    await consume(chunk)

For server-pushed watches (watch_tasks, watch_memories, tail, subscribe_logs, etc.):

# Before — sync
it = adapter.watch_memories(source="bot")
for batch in it:
    render(batch)

# After — async
async_adapter = AsyncMemoriesAdapter(adapter)
it = await async_adapter.watch_memories(source="bot")
async for batch in it:
    await render(batch)

The sync Adapter.watch_memories(...) returns an iterator directly; the async equivalent is an awaitable yielding an async iterator (one extra await), because the underlying stream construction itself may run on the bridge runtime.

Net Encrypted UDP Transport

Net provides encrypted point-to-point UDP transport for high-performance scenarios:

from net import Net, generate_net_keypair
import os

# Generate keypair for responder
keypair = generate_net_keypair()
psk = os.urandom(32).hex()

# Responder side
responder = Net(
    num_shards=2,
    net_bind_addr='127.0.0.1:9001',
    net_peer_addr='127.0.0.1:9000',
    net_psk=psk,
    net_role='responder',
    net_secret_key=keypair.secret_key,
    net_public_key=keypair.public_key,
    net_reliability='light',  # 'none', 'light', or 'full'
)

# Initiator side (knows responder's public key)
initiator = Net(
    num_shards=2,
    net_bind_addr='127.0.0.1:9000',
    net_peer_addr='127.0.0.1:9001',
    net_psk=psk,
    net_role='initiator',
    net_peer_public_key=keypair.public_key,
)

# Use as normal
initiator.ingest_raw('{"event": "data"}')

Backpressure Modes

  • "drop_newest" - Reject new events when buffer is full
  • "drop_oldest" - Evict oldest events to make room
  • "fail_producer" - Raise an error

NAT traversal (optimization, not correctness)

Two NATed peers already reach each other through the mesh's routed-handshake path. NAT traversal opens a shorter direct path when the NAT shape allows it; it's never required for connectivity. Every method below is safe to call regardless of NAT type — a failed punch or a traversal: * RuntimeError is not a connectivity failure, traffic keeps riding the relay. The whole surface is a no-op when the native module was built without --features nat-traversal: every call raises RuntimeError("traversal: unsupported").

from net import NetMesh

mesh = NetMesh(bind_addr="0.0.0.0:9000", psk="00" * 32)

mesh.reclassify_nat()

klass  = mesh.nat_type()            # "open" | "cone" | "symmetric" | "unknown"
reflex = mesh.reflex_addr()         # "203.0.113.5:9001" or None

observed = mesh.probe_reflex(peer_node_id)   # "ip:port"

# Attempt a direct connection via the pair-type matrix.
# `coordinator` mediates the punch when the matrix picks one.
# Always returns — inspect stats to learn which path won.
mesh.connect_direct(peer_node_id, peer_pubkey_hex, coordinator_node_id)

# Cumulative counters — all int, monotonic.
s = mesh.traversal_stats()
s.punches_attempted   # coordinator mediated a PunchRequest + Introduce
s.punches_succeeded   # ack arrived AND direct handshake landed
s.relay_fallbacks     # landed on the routed path after skip/fail

Operators with a known-public address skip the classifier sweep entirely. The override pins "open" + the supplied address on every capability announcement; call announce_capabilities() after to propagate (the setter resets the rate-limit floor so the next announce is guaranteed to broadcast).

mesh.set_reflex_override('203.0.113.5:9001')
mesh.announce_capabilities(caps)
# later:
mesh.clear_reflex_override()
mesh.announce_capabilities(caps)

Traversal failures surface as RuntimeError with a stable traversal: <kind>[: <detail>] message prefix. The <kind> discriminator is one of reflex-timeout | peer-not-reachable | transport | rendezvous-no-relay | rendezvous-rejected | punch-failed | port-map-unavailable | unsupported. Match on the prefix for machine-readable branching:

try:
    mesh.connect_direct(peer_node_id, peer_pubkey_hex, coord_id)
except RuntimeError as e:
    msg = str(e)
    if msg.startswith("traversal: unsupported"):
        ...   # native module built without --features nat-traversal
    elif msg.startswith("traversal: peer-not-reachable"):
        ...

"unsupported" is the signal that the bindings are linked unconditionally and the native module doesn't have the feature — callers can branch cleanly without probing for symbol presence.

Channels (distributed pub/sub)

Named pub/sub over the encrypted mesh. Publishers register channels with access policy; subscribers ask to join via a membership subprotocol; publish fans payloads out to every current subscriber.

from net import NetMesh, ChannelAuthError, ChannelError

pub = NetMesh('127.0.0.1:9001', '42' * 32)
try:
    pub.register_channel(
        'sensors/temp',
        visibility='global',      # or 'subnet-local' | 'parent-visible' | 'exported'
        reliable=True,
        priority=2,
        max_rate_pps=1000,
    )

    # Subscriber side (after handshake with pub):
    # sub.subscribe_channel(pub.node_id, 'sensors/temp')

    # Fan a payload out to all subscribers.
    report = pub.publish(
        'sensors/temp',
        b'{"celsius": 22.5}',
        reliability='reliable',
        on_failure='best_effort',
        max_inflight=32,
    )
    print(f"{report['delivered']}/{report['attempted']} subscribers received")
finally:
    pub.shutdown()

# Typed errors for ACL outcomes:
# try: sub.subscribe_channel(peer_id, 'restricted')
# except ChannelAuthError: ...   # publisher denied
# except ChannelError: ...       # unknown channel / other rejection

Channel names always cross the binding as strings (not the u16 hash) to avoid ACL bypass via collision. The Python binding does not yet expose a dedicated per-channel receive API; that is a follow-up.

CortEX & NetDb (event-sourced state)

Typed, event-sourced state on top of RedEX — tasks and memories with filterable queries and sync watch iterators. Includes the snapshot_and_watch primitive whose race fix landed on v2, so you can safely "paint what's there now, then react to changes" without losing updates that race during construction.

from net import NetDb, CortexError

db = NetDb.open(origin_hash=0xABCDEF01, with_tasks=True, with_memories=True)
tasks = db.tasks

try:
    seq = tasks.create(1, 'write docs', 100)
    tasks.wait_for_seq(seq)   # block until the fold has applied
except CortexError as e:
    # adapter-level failure (RedEX I/O, fold halted, etc.)
    ...

# Snapshot + watch, one atomic call — no race.
snap, it = tasks.snapshot_and_watch_tasks(status='pending')
print('initial:', len(snap), 'pending tasks')
for batch in it:
    print('update:', len(batch), 'pending tasks')
    if len(batch) == 0:
        it.close()    # idempotent; ends the iterator
        break

db.close()

Standalone adapters

If you only need one model, skip the NetDb facade:

from net import Redex, TasksAdapter

redex = Redex(persistent_dir='/var/lib/net/redex')
tasks = TasksAdapter.open(redex, origin_hash=0xABCDEF01, persistent=True)

MemoriesAdapter exposes the same shape with store / retag / pin / unpin / delete / list_memories / watch_memories / snapshot_and_watch_memories.

Raw RedEX file (no CortEX fold)

For domain-agnostic persistent logs — your own event schema, no fold, no typed adapter — open a RedexFile directly from a Redex. The tail is a sync Python iterator; call close() or let StopIteration fire when the file closes.

from net import Redex, RedexError

redex = Redex(persistent_dir='/var/lib/net/events')
file = redex.open_file(
    'analytics/clicks',
    persistent=True,
    fsync_interval_ms=100,           # or fsync_every_n=1000
    retention_max_events=1_000_000,
)

# Append (or batch-append).
seq = file.append(b'{"url": "/home"}')
# `append_batch` returns the first-seq int of the batch, or `None`
# for an empty input. The `None` return is the explicit "I
# appended nothing" signal — pre-`bugfixes-8` it returned `0`,
# which collided with the legitimate "first event of a non-empty
# batch landed at seq 0" return.
first = file.append_batch([b'{"a": 1}', b'{"a": 2}'])

# Tail — backfills the retained range, then streams live appends.
try:
    for event in file.tail(from_seq=0):
        print(event.seq, bytes(event.payload))
        if should_stop:
            break           # idempotent; ends the iterator via close()
except RedexError as e:
    ...

file.close()

Errors from the RedEX surface raise RedexError (invalid channel name, bad config, append / tail / sync / close failures).

Cross-node RedEX replication

RedEX channels can replicate across the mesh. Opt in per channel by passing replication=True plus tunables on the open_file call. The default (no replication kwarg) keeps the channel single-node and adds zero wire traffic. Replicated channels carry N copies of the log; the leader is the single writer, replicas catch up via pull-based sync. Failover uses a deterministic nearest-RTT election with NodeId tie-break.

from net import NetMesh, Redex

mesh = NetMesh(bind_addr='127.0.0.1:0', psk='...')
redex = Redex(persistent_dir='/var/lib/net/events')

# Install the per-Redex replication router on the mesh.
# Idempotent — safe to call from multiple paths.
redex.enable_replication(mesh)

file = redex.open_file(
    'orders/audit',
    persistent=True,
    replication=True,
    replication_factor=3,                 # 1..16; default 3
    replication_heartbeat_ms=500,         # min 100; default 500
    replication_placement='standard',     # 'standard' | 'pinned' | 'colocation_strict'
    # replication_pinned_nodes=[node_a, node_b, node_c],   # required when placement='pinned'
    # replication_leader_pinned=some_node_id,
    replication_on_under_capacity='withdraw',   # 'withdraw' (default) | 'evict_oldest'
    replication_budget_fraction=0.5,
)
file.append(b'event payload')

The leader handles every append locally; replicas observe the leader's heartbeat tail_seq, issue SYNC_REQUEST on lag, apply chunks via SYNC_RESPONSE. When the leader closes (or the replica's believed leader goes silent past 3 × replication_heartbeat_ms), the surviving replicas run the deterministic election and one becomes the new leader within microseconds.

Operator surface on Redex:

  • enable_replication(mesh) — install replication wiring. Required before open_file(..., replication=True).
  • replication_runtime_count() -> int — registered per-channel runtimes.
  • replication_prometheus_text() -> str — Prometheus-text render of the seven per-channel metric shapes (*_lag_seconds, *_sync_bytes_total, *_leader_changes_total, *_under_capacity_total, *_skip_ahead_total, *_election_thrash_total, *_witness_withdrawals_total). Returns the empty string when replication isn't enabled; pipe straight into an HTTP scrape body without branching.
# HTTP scrape handler (Flask example)
@app.route('/metrics')
def metrics():
    return redex.replication_prometheus_text(), 200, {'Content-Type': 'text/plain'}

Disk-pressure handling: when a replica's local file rejects an append (heap-segment cap or disk write-fail), the configured replication_on_under_capacity policy fires — 'withdraw' drops the replica role (capability tag withdrawn; peers re-route to a healthy holder); 'evict_oldest' runs retention sweep + retries (requires retention_max_* kwargs to be set on the same call).

Validation surfaces RedexError before the call reaches the underlying coordinator, so misconfigured replication_factor (out of [1, 16]), replication_placement='pinned' without replication_pinned_nodes, or unknown enum strings fail fast.

Why snapshot_and_watch_*?

Calling list_tasks() then watch_tasks() takes two independent state reads. A mutation landing between them would be silently lost under the old skip(1) implementation. The atomic primitive returns the snapshot and an iterator seeded so that any divergent initial emission is forwarded through instead of dropped — see docs/STORAGE_AND_CORTEX.md.

Dataforts (greedy cache, gravity, blob refs, read-your-writes)

Dataforts is the compositional data plane on top of RedEX / CortEX / capability-index / proximity-graph. The PyO3 module surfaces it as Redex methods, top-level blob helpers, and a WriteToken + wait_for_token pair on Tasks / Memories. Built behind the dataforts Cargo feature; pre-built wheels on PyPI ship with the feature enabled. A build without it raises RedexError from enable_greedy_dataforts(...) with "requires the 'dataforts' feature; rebuild with --features dataforts" rather than failing silently.

Four phases:

  • Phase 1 — Greedy-LRU caching. Five-axis admission (scope + proximity + capability-preference + colocation + storage-cap)
    • bandwidth budget gate per-channel cache files. Cold channels evict under cluster-cap pressure and withdraw their causal:<hex> advertisement.
  • Phase 3 — BlobRef + blob adapters. A 4-byte-magic + version + 32-byte BLAKE3 + size + URI reference whose bytes live in the caller's storage (S3 / Ceph / IPFS / local FS). Adapters implement fetch / store (sync or async def); the filesystem adapter ships in-tree via register_filesystem_blob_adapter. Custom adapters register through register_blob_adapter(adapter_id, instance). Async adapters run on a binding-owned event loop on a dedicated thread — adapter coroutines never share the calling thread's event loop, so an aiobotocore / httpx.AsyncClient / SQLAlchemy async engine inside the adapter is safe.
  • Phase 4 — Data gravity. Per-chain read-rate counters with exponential decay. Threshold-crossing emissions stamp heat:<hex>=<rate> onto the chain's capability announcement; greedy weights cache pulls by heat × scope-match × proximity.
  • Phase 5 — Read-your-writes. Every tasks.create, memories.insert, etc. returns a WriteToken { origin_hash, seq } (frozen, hashable; constructor is #[pyo3(doc_hidden)] — tokens are unforgeable only against the issuing adapter via origin binding). tasks.wait_for_token(token, deadline_ms=…) blocks until the local fold has applied that seq; tracks both applied_through_seq and folded_through_seq so a stalled fold raises CortexError. deadline_ms=0 is a non-blocking poll (no scheduling).
from net import (
    NetMesh, Redex, Tasks,
    register_filesystem_blob_adapter, blob_publish, blob_resolve,
)

mesh = NetMesh(bind_addr='0.0.0.0:7000', psk='...')
redex = Redex(persistent_dir='/var/lib/net/redex')

# Phase 1 — wire greedy into the mesh inbound dispatch.
redex.enable_greedy_dataforts(
    mesh,
    scopes=['region:us'],
    total_cap_bytes=1 << 30,         # 1 GiB cluster-cap
    per_channel_cap_bytes=64 << 20,
)

# Phase 4 — layer gravity on top.
redex.enable_gravity_for_greedy(
    mesh,
    emit_threshold_ratio=1.5,
    decay_half_life_secs=300,
)

# Phase 3 — filesystem adapter (ships in-tree).
register_filesystem_blob_adapter('local', '/var/blobs')
ref = blob_publish('local', 'local://obj/payload', some_bytes)
back = blob_resolve(ref)

# Phase 5 — read-your-writes.
tasks = Tasks.open(redex, origin_hash=mesh.origin_hash)
result = tasks.create(1, 'first', 100)
tasks.wait_for_token(result.token, deadline_ms=250)
# tasks.wait_for_token(token, deadline_ms=0) → non-blocking poll

# Diagnostics.
print(redex.greedy_cached_channel_count())
print(redex.greedy_prometheus_text())

The canonical channel hash is 64-bit (channel_hash(name) returns int in the u64 range). The per-packet wire NetHeader channel_hash stays u16 — fast-path filter hint, may bucket-collide at scale; ACL / config / cache / RYW decisions key on the canonical 64-bit hash via registry disambiguation. The PermissionToken wire form is 169 bytes (PermissionToken::WIRE_SIZE in the Rust core).

Redis Streams consumer-side dedup helper

The Net Redis adapter writes a stable dedup_id field on every XADD entry: {producer_nonce:hex}:{shard_id}:{sequence_start}:{i}. Combined with the bus's persistent producer-nonce path (producer_nonce_path on EventBusConfig), the id is stable across both within-process retries AND cross-process restart — the MULTI/EXEC-timeout race becomes filterable at consume time.

RedisStreamDedup is the consumer-side helper, exposed on the net PyO3 module:

from net import RedisStreamDedup
import redis

# ~10k events/sec * 1 min dedup window → ~600,000.
dedup = RedisStreamDedup(capacity=600_000)

r = redis.Redis(host="localhost", port=6379)
cursor = "0"
while True:
    # XRANGE bounds are INCLUSIVE on both ends. After the first
    # page we must use the exclusive form `(<id>` so we don't
    # re-read the entry the cursor points at — a vanilla
    # `min=cursor` loop spins forever once the cursor reaches the
    # tail and the same entry is returned every iteration.
    start = cursor if cursor == "0" else f"({cursor}"
    entries = r.xrange("net:shard:0", min=start, max="+", count=100)
    for entry_id, fields in entries:
        dedup_id = fields.get(b"dedup_id", b"").decode()
        if not dedup_id:
            # No dedup_id → older entry or non-Net producer; skip
            # dedup and process as-is.
            process(entry_id, fields)
            continue
        if not dedup.is_duplicate(dedup_id):
            process(entry_id, fields)
        cursor = entry_id.decode()
    if not entries:
        break

Surface:

dedup = RedisStreamDedup()                # default capacity 4096
dedup = RedisStreamDedup(capacity=N)      # explicit; 0 → 1
dedup.is_duplicate(id: str) -> bool       # test-and-insert
dedup.len                                 # property — tracked-id count
dedup.capacity                            # property — configured cap
dedup.is_empty                            # property
dedup.clear()                             # reset (e.g. on consumer-group rebalance)

The helper is transport-agnostic — bring your own redis-py / aioredis / equivalent client; it just answers the dedup question against an in-memory LRU. Concurrency: each handle wraps a Rust Mutex<RedisStreamDedup>, so concurrent calls from multiple Python threads are safe but serialize. Production-shape is one helper per consumer thread.

Security Surface (Stage A–E)

The mesh layer surfaces the same identity / capabilities / subnets / channel-auth story that the Rust SDK and the TypeScript / Node SDKs ship. Full staging and rationale: docs/SDK_SECURITY_SURFACE_PLAN.md. Python-binding parity details: docs/SDK_PYTHON_PARITY_PLAN.md.

Identity + permission tokens

Every node has an ed25519 identity; permission tokens are ed25519- signed delegations that authorize a subject to publish / subscribe / delegate / admin on a channel, optionally with further delegation depth.

from net import Identity, parse_token, verify_token, delegate_token

alice = Identity.generate()
bob = Identity.generate()

# Alice issues Bob a subscribe+delegate token good for 5 min, with
# one re-delegation hop remaining. `ttl_seconds=0` raises
# `TokenError` — a zero TTL would mint a born-expired token that
# every receiver would reject as `Expired`, leaving the issuer to
# diagnose the misuse from receiver-side log lines.
token = alice.issue_token(
    subject=bob.entity_id,
    scope=["subscribe", "delegate"],
    channel="sensors/temp",
    ttl_seconds=300,
    delegation_depth=1,
)
assert verify_token(token) is True

# Bob re-delegates to Carol; depth drops to 0 (leaf).
carol = Identity.generate()
child = delegate_token(bob, token, carol.entity_id, ["subscribe"])
assert parse_token(child)["delegation_depth"] == 0

Capability announcements + peer discovery

Announce hardware / software / model / tool / tag fingerprints, then query the local capability index with a filter.

mesh.announce_capabilities({
    "hardware": {
        "cpu_cores": 16,
        "memory_gb": 64,
        "gpu": {"vendor": "nvidia", "model": "h100", "vram_gb": 80},
    },
    "models": [{"model_id": "llama-3.1-70b", "family": "llama",
                "context_length": 128_000}],
    "tags": ["gpu", "prod"],
})

gpu_peers = mesh.find_nodes({
    "require_gpu": True,
    "gpu_vendor": "nvidia",
    "min_vram_gb": 40,
})

Scoped discovery (reserved scope:* tags)

A provider can narrow who its query result reaches by tagging its CapabilitySet with reserved scope:* tags. Queries call mesh.find_nodes_scoped(filter, scope) to filter candidates. The wire format and forwarders are untouched — enforcement is purely query-side.

# GPU pool advertised to one tenant only.
mesh.announce_capabilities({
    "tags": ["model:llama3-70b", "scope:tenant:oem-123"],
})

# Tenant-scoped query — returns this node + any Global (untagged) peers.
oem_nodes = mesh.find_nodes_scoped(
    {"require_tags": ["model:llama3-70b"]},
    {"kind": "tenant", "tenant": "oem-123"},
)

Accepted scope dict shapes: {"kind": "any"} (default), {"kind": "global_only"}, {"kind": "same_subnet"}, {"kind": "tenant", "tenant": "<id>"}, {"kind": "tenants", "tenants": [...]}, {"kind": "region", "region": "<name>"}, {"kind": "regions", "regions": [...]}. Reserved announcement tags: scope:subnet-local (visible only under same_subnet), scope:tenant:<id>, scope:region:<name> — strictest scope wins. Untagged peers resolve to Global and stay visible under permissive queries. Full design: docs/SCOPED_CAPABILITIES_PLAN.md.

Capability propagation is multi-hop, bounded by MAX_CAPABILITY_HOPS = 16 with (origin, version) dedup on every forwarder. capability_gc_interval_ms controls both the index TTL sweep and the dedup cache eviction. See docs/MULTIHOP_CAPABILITY_PLAN.md.

Subnets

Nodes can bind to a hierarchical SubnetId (1–4 levels, each 0–255) directly, or derive one from announced tags via a SubnetPolicy.

# Explicit subnet.
mesh = NetMesh("127.0.0.1:9000", PSK, subnet=[3, 7, 2])

# Or derive from tags.
mesh = NetMesh(
    "127.0.0.1:9001", PSK,
    subnet_policy={
        "rules": [
            {"tag_prefix": "region:", "level": 0,
             "values": {"eu": 1, "us": 2, "apac": 3}},
            {"tag_prefix": "zone:", "level": 1,
             "values": {"a": 1, "b": 2, "c": 3}},
        ]
    },
)

Channel authentication

Publishers set publish_caps / subscribe_caps / require_token on register_channel. Subscribers present a PermissionToken via the optional token=bytes kwarg on subscribe_channel.

mesh.register_channel(
    "gpu/jobs",
    subscribe_caps={"require_gpu": True, "min_vram_gb": 16},
    require_token=True,
)

# Subscriber side, with a token issued by the publisher:
mesh.subscribe_channel(publisher_node_id, "gpu/jobs", token=token_bytes)

Denied subscribes raise ChannelAuthError (a subclass of ChannelError); malformed tokens raise TokenError whose message has the form "token: <kind>" (invalid_signature, expired, delegation_exhausted, …). Successful subscribes populate an AuthGuard bloom filter on the publisher so every subsequent publish admits the subscriber in constant time. An expiry sweep (default 30 s) evicts subscribers whose tokens age out; a per- peer auth-failure rate limiter throttles bad-token storms. Cross- SDK behaviour is fixed by the Rust integration suite; see tests/channel_auth.rs and tests/channel_auth_hardening.rs.

nRPC (request / response over the mesh)

nRPC is the request/response convention layer riding on top of the pub/sub mesh + CortEX folds. Built with the cortex feature (maturin's default picks it up). The native module exposes two layers:

  • Raw bytesnet.MeshRpc (pyclass): serve(service, fn) -> ServeHandle, call(target, service, bytes) -> bytes, call_service(service, bytes) -> bytes, call_streaming(target, service, bytes) -> RpcStream, find_service_nodes(service) -> list[int]. Synchronous calls release the GIL across runtime.block_on(...) so other Python threads run; handler callbacks dispatch under tokio::task::spawn_blocking so GIL acquisition doesn't starve the runtime.
  • Typed wrappernet.mesh_rpc.TypedMeshRpc: JSON encode/decode at the binding boundary so user code works with plain Python objects (dicts, lists, strings, numbers, bools, None). The default codec is json.dumps / json.loads — to send dataclasses, Pydantic models, or numpy arrays, convert to a JSON-serializable shape before calling (e.g. dataclasses.asdict(x) or model.model_dump()); a non- serializable input raises RpcCodecError before the call hits the wire so the diagnostic points at the call site rather than the server. Resilience helpers (RetryPolicy, HedgePolicy, CircuitBreaker) plus the typed exception classes (RpcNoRouteError, RpcTimeoutError, RpcServerError, RpcTransportError, RpcCodecError, RpcAppError, BreakerOpenError) live here too.
from net import NetMesh
from net.mesh_rpc import (
    NRPC_TYPED_BAD_REQUEST,
    RetryPolicy,
    RpcServerError,
    TypedMeshRpc,
)

server = NetMesh("127.0.0.1:9001", "42" * 32)
client = NetMesh("127.0.0.1:9000", "42" * 32)
# (handshake omitted — see "Net Encrypted UDP Transport")

# Server side: register a typed handler. ServeHandle is a context
# manager — `with` ensures unregister on exit.
server_rpc = TypedMeshRpc.from_mesh(server)
def echo_sum(req: dict) -> dict:
    return {"echo": req["text"], "sum": sum(req["numbers"])}

with server_rpc.serve("echo_sum", echo_sum):
    client_rpc = TypedMeshRpc.from_mesh(client)
    reply = client_rpc.call(
        server.node_id(), "echo_sum",
        {"text": "hi", "numbers": [1, 2, 3]},
        opts={"deadline_ms": 200},
    )
    # reply == {"echo": "hi", "sum": 6}

# Streaming responses iterate decoded chunks until EOF or terminal error.
# `stream_window_initial` is the canonical key; `stream_window` is an
# alias accepted for ergonomic parity.
stream = client_rpc.call_streaming(
    target_node_id, "tail", {"tail": "events"},
    opts={"deadline_ms": 5000, "stream_window_initial": 8},
)
for chunk in stream:
    process(chunk)
# stream.close() emits CANCEL to the server (best-effort).
# stream.grant(n) issues an explicit credit publish for batched cadence.

# Resilience helpers: retry / hedge / circuit breaker.
policy = RetryPolicy(max_attempts=4, initial_backoff_ms=50, max_backoff_ms=1000)
client_rpc.call_with_retry(target_node_id, "echo", {"hi": 1}, policy)

Status codes + error model

Every caller-side failure is a typed exception whose message carries the canonical nrpc:<kind>: <detail> prefix. The set of kinds is fixed by the cross-binding contract: no_route, timeout, server_error, transport, codec_encode, codec_decode, breaker_open. The classify_error(e) helper in net.mesh_rpc extracts the kind string from a caught exception's message — useful for fallback paths where discriminating without isinstance is awkward (e.g. when the native module wasn't built and every typed alias collapses to plain Exception):

from net.mesh_rpc import classify_error

try:
    rpc.call(target, "echo", body, opts={"deadline_ms": 200})
except Exception as e:
    kind = classify_error(e)
    if kind == "no_route":
        ...      # capability index didn't surface a target
    elif kind == "timeout":
        ...      # caller-side deadline elapsed
    elif kind == "server_error":
        ...      # str(e) carries `status=0xNNNN message=...`
Constant Hex Meaning
NRPC_TYPED_BAD_REQUEST 0x8000 Typed handler couldn't decode the request body.
NRPC_TYPED_HANDLER_ERROR 0x8001 Typed handler ran but returned an exception.

Cross-binding contract spec — including the canonical cross_lang_echo_sum service used by every binding's wire-format compat test — lives in ../../README.md#nrpc. The Python binding's own compat suite is at tests/test_cross_lang_compat.py.

Compute (daemons + migration)

Run MeshDaemons directly from Python. DaemonRuntime owns the factory table, the per-daemon hosts, and the Registering → Ready → ShuttingDown lifecycle gate that decides when inbound migrations may land. Daemons are any Python object whose process(event) returns a list of bytes/bytearray payloads — the runtime wraps each output in a causal link and forwards it.

Build the native module with the compute feature (maturin picks it up on the default build) and import from net. Full design notes: docs/SDK_COMPUTE_SURFACE_PLAN.md.

from net import DaemonRuntime, NetMesh, Identity, CausalEvent


class EchoDaemon:
    """Stateless echo — ships every event's payload straight back."""

    name = "echo"

    def process(self, event: CausalEvent) -> list[bytes]:
        return [bytes(event.payload)]

    # Optional: snapshot() / restore(state) for migration-capable daemons.


mesh = NetMesh("127.0.0.1:9000", "42" * 32)
rt = DaemonRuntime(mesh)

# 1. Register factories BEFORE flipping the runtime to Ready.
rt.register_factory("echo", lambda: EchoDaemon())

# 2. Ready the runtime — after this point spawn / migration accept.
rt.start()

# 3. Spawn a daemon; Identity pins the ed25519 keypair so
#    origin_hash / entity_id stay stable across migrations.
identity = Identity.generate()
handle = rt.spawn("echo", identity)
print(f"origin = 0x{handle.origin_hash:08x}")

# 4. Manually feed an event for testing; real delivery happens
#    via the mesh's causal chain.
event = CausalEvent(handle.origin_hash, sequence=1, payload=b"hello")
outputs = rt.deliver(handle.origin_hash, event)

# 5. Clean shutdown.
rt.stop(handle.origin_hash)
rt.shutdown()

process must be synchronous — the core's contract is sync, and the PyO3 bridge re-attaches the GIL for the duration of each call. Raising inside process surfaces as DaemonError on the caller.

Migration

start_migration(origin_hash, source_node, target_node) orchestrates the six-phase cutover (Snapshot → Transfer → Restore → Replay → Cutover → Complete). The source seals the daemon's ed25519 seed into the outbound snapshot using the target's X25519 static pubkey; the target rebuilds the daemon via the factory registered under the same kind, replays any events that arrived during transfer, then activates.

from net import MigrationError, migration_error_kind

try:
    mig = rt.start_migration(handle.origin_hash, src_node_id, dst_node_id)
    # mig.phase  — "snapshot" | "transfer" | "restore" | ...
    # mig.source_node / mig.target_node
    mig.wait()                      # blocks to completion
except MigrationError as e:
    kind = migration_error_kind(e)
    if kind == "not-ready":               ...  # target start() didn't run
    elif kind == "factory-not-found":     ...  # target missing this kind
    elif kind == "compute-not-supported": ...  # target has no DaemonRuntime
    elif kind == "state-failed":          ...  # snapshot / restore threw
    elif kind == "identity-transport-failed": ...  # seal / unseal failed
    # ...see SDK_COMPUTE_SURFACE_PLAN.md for the full enum

start_migration_with(origin, src, dst, opts) exposes options such as seal_seed=False for test scenarios. On the target node, call rt.register_migration_target_identity(kind, identity) before any migration of that kind lands; without it the runtime rejects sealed-seed envelopes with migration_error_kind == "identity-transport-failed".

Surface at a glance

Method Description
DaemonRuntime(mesh) Construct against an existing NetMesh
rt.register_factory(kind, fn) Install a factory (before start())
rt.start() / rt.shutdown() Flip the lifecycle gate
rt.spawn(kind, identity, config=None) Spawn a local daemon
rt.spawn_from_snapshot(kind, identity, bytes, config=None) Rehydrate
rt.stop(origin) Stop a local daemon
rt.snapshot(origin) Capture bytes for persistence / migration
rt.deliver(origin, event) Feed an event (returns list[bytes])
rt.start_migration(origin, src, dst) Orchestrate a live migration
rt.register_migration_target_identity(kind, id) Pin unseal keypair on target for kind
handle.origin_hash / entity_id / stats() Per-daemon identity + stats
DaemonError / MigrationError Typed exceptions; migration_error_kind(e) parses e.kind

Compute Groups (Replica / Fork / Standby)

HA / scaling overlays on top of DaemonRuntime. Build the native module with the groups feature (implies compute) to expose ReplicaGroup, ForkGroup, StandbyGroup, and the GroupError exception class.

  • ReplicaGroup — N interchangeable copies of a daemon. Deterministic identity from group_seed + index, so a replacement respawned on another node has a stable origin_hash. Load-balances inbound events across healthy members; auto-replaces on node failure.
  • ForkGroup — N independent daemons forked from a common parent at fork_seq. Unique keypairs, shared ancestry via a verifiable ForkRecord.
  • StandbyGroup — active-passive replication. One member processes events; standbys hold snapshots and catch up via sync_standbys(). On active failure the most-synced standby promotes and replays the events buffered since the last sync.
from net import (
    DaemonRuntime, ForkGroup, GroupError, ReplicaGroup, StandbyGroup,
    group_error_kind,
)

rt = DaemonRuntime(mesh)
rt.register_factory("counter", lambda: CounterDaemon())

# --- ReplicaGroup ----------------------------------------------------
replicas = ReplicaGroup.spawn(
    rt, "counter",
    replica_count=3,
    group_seed=bytes([0x11] * 32),
    lb_strategy="consistent-hash",   # or "round-robin" / "least-load"
                                     #    / "least-connections" / "random"
)
origin = replicas.route_event({"routing_key": "user:42"})
rt.deliver(origin, event)
replicas.scale_to(5)                 # grow
replicas.on_node_failure(failed_node_id)   # respawn elsewhere

# --- ForkGroup -------------------------------------------------------
forks = ForkGroup.fork(
    rt, "counter",
    parent_origin=0xABCDEF01,
    fork_seq=42,
    fork_count=3,
    lb_strategy="round-robin",
)
assert forks.verify_lineage()
for record in forks.fork_records():
    print(record["forked_origin"], record["fork_seq"])

# --- StandbyGroup ----------------------------------------------------
hot = StandbyGroup.spawn(
    rt, "counter",
    member_count=3,                  # 1 active + 2 standbys
    group_seed=bytes([0x77] * 32),
)
rt.deliver(hot.active_origin, event)
hot.sync_standbys()                  # periodic catchup
# On active-node failure:
# new_origin = hot.on_node_failure(failed_node_id)  # auto-promotes

Typed errors

Failures raise GroupError (a subclass of DaemonError). Use group_error_kind(e) to parse the discriminator from the Rust side's daemon: group: <kind>[: detail] message prefix:

from net import GroupError, group_error_kind

try:
    ReplicaGroup.spawn(rt, "never-registered",
                       replica_count=2, group_seed=bytes(32))
except GroupError as e:
    kind = group_error_kind(e)
    if kind == "not-ready":               ...  # runtime.start() didn't run
    elif kind == "factory-not-found":     ...  # kind wasn't registered
    elif kind == "no-healthy-member":     ...  # routed on an all-down group
    elif kind == "invalid-config":        ...  # e.g. replica_count == 0
    elif kind in ("placement-failed",
                  "registry-failed",
                  "daemon"):              ...  # core failure — read e args

Full staging, wire formats, and rationale: docs/SDK_GROUPS_SURFACE_PLAN.md. Core semantics live in the main README.md#daemons.

Performance Tips

  1. Use ingest_raw() for maximum throughput - Pass pre-serialized JSON strings
  2. Use ingest_raw_batch() for bulk operations - Reduces per-call overhead
  3. Increase ring_buffer_capacity - Larger buffers handle bursts better
  4. Match num_shards to CPU cores - Default is optimal for most cases

Cargo features

The five feature flags that gate the storage / query / OS surfaces on this binding. Wheels built without a feature silently omit its symbols — the build never warns. bindings/python/python/net/__init__.py try-imports each block individually, so from net import Redex raises ImportError (not AttributeError) when the underlying _net extension was compiled without cortex.

Feature Surface enabled in the PyO3 module
cortex Redex, RedexFile, TasksAdapter, MemoriesAdapter, NetDb, Task, Memory, watch iterators, RedexError, CortexError, NetDbError
redex-disk Disk-backed persistence for RedEX — the persistent_dir ctor arg and Persistent: true on open_file. Without it the persistent path returns RedexError.
netdb NetDb composition over Tasks + Memories (requires cortex). The net_netdb_* FFI entry points ship with this feature.
meshdb MeshQuery, MeshQueryRunner, QueryBuilder, Predicate, InMemoryChainReader, the rest of the query layer, plus the libnet_meshdb cdylib.
meshos MeshOsDaemonSdk, MeshOsDaemonHandle, plus the libnet_meshos cdylib.

Enable at build time:

# Dev install into the active venv:
maturin develop --features "cortex netdb redex-disk meshdb meshos"

# Or produce a wheel:
maturin build --release --features "cortex netdb redex-disk meshdb meshos"

PyPI wheels published as net-mesh ship with every feature enabled; the flags above only matter for source builds.

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

net_mesh-0.27.5.tar.gz (6.7 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

net_mesh-0.27.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded PyPymanylinux: glibc 2.28+ x86-64

net_mesh-0.27.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl (4.9 MB view details)

Uploaded PyPymanylinux: glibc 2.28+ ARM64

net_mesh-0.27.5-cp315-cp315t-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.15tmanylinux: glibc 2.28+ x86-64

net_mesh-0.27.5-cp315-cp315-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.15manylinux: glibc 2.28+ x86-64

net_mesh-0.27.5-cp314-cp314t-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.28+ x86-64

net_mesh-0.27.5-cp314-cp314t-manylinux_2_28_aarch64.whl (4.9 MB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.28+ ARM64

net_mesh-0.27.5-cp314-cp314-win_arm64.whl (4.1 MB view details)

Uploaded CPython 3.14Windows ARM64

net_mesh-0.27.5-cp314-cp314-win_amd64.whl (4.4 MB view details)

Uploaded CPython 3.14Windows x86-64

net_mesh-0.27.5-cp314-cp314-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.28+ x86-64

net_mesh-0.27.5-cp314-cp314-manylinux_2_28_aarch64.whl (4.9 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.28+ ARM64

net_mesh-0.27.5-cp314-cp314-macosx_11_0_arm64.whl (4.4 MB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

net_mesh-0.27.5-cp314-cp314-macosx_10_12_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.14macOS 10.12+ x86-64

net_mesh-0.27.5-cp313-cp313-win_arm64.whl (4.1 MB view details)

Uploaded CPython 3.13Windows ARM64

net_mesh-0.27.5-cp313-cp313-win_amd64.whl (4.5 MB view details)

Uploaded CPython 3.13Windows x86-64

net_mesh-0.27.5-cp313-cp313-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ x86-64

net_mesh-0.27.5-cp313-cp313-manylinux_2_28_aarch64.whl (4.9 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ ARM64

net_mesh-0.27.5-cp313-cp313-macosx_11_0_arm64.whl (4.5 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

net_mesh-0.27.5-cp313-cp313-macosx_10_12_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

net_mesh-0.27.5-cp312-cp312-win_arm64.whl (4.1 MB view details)

Uploaded CPython 3.12Windows ARM64

net_mesh-0.27.5-cp312-cp312-win_amd64.whl (4.5 MB view details)

Uploaded CPython 3.12Windows x86-64

net_mesh-0.27.5-cp312-cp312-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.28+ x86-64

net_mesh-0.27.5-cp312-cp312-manylinux_2_28_aarch64.whl (4.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.28+ ARM64

net_mesh-0.27.5-cp312-cp312-macosx_11_0_arm64.whl (4.4 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

net_mesh-0.27.5-cp312-cp312-macosx_10_12_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.12macOS 10.12+ x86-64

net_mesh-0.27.5-cp311-cp311-win_arm64.whl (4.2 MB view details)

Uploaded CPython 3.11Windows ARM64

net_mesh-0.27.5-cp311-cp311-win_amd64.whl (4.5 MB view details)

Uploaded CPython 3.11Windows x86-64

net_mesh-0.27.5-cp311-cp311-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.28+ x86-64

net_mesh-0.27.5-cp311-cp311-manylinux_2_28_aarch64.whl (4.9 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.28+ ARM64

net_mesh-0.27.5-cp311-cp311-macosx_11_0_arm64.whl (4.5 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

net_mesh-0.27.5-cp311-cp311-macosx_10_12_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.11macOS 10.12+ x86-64

net_mesh-0.27.5-cp310-cp310-win_amd64.whl (4.5 MB view details)

Uploaded CPython 3.10Windows x86-64

net_mesh-0.27.5-cp310-cp310-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.28+ x86-64

net_mesh-0.27.5-cp310-cp310-manylinux_2_28_aarch64.whl (4.9 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.28+ ARM64

File details

Details for the file net_mesh-0.27.5.tar.gz.

File metadata

  • Download URL: net_mesh-0.27.5.tar.gz
  • Upload date:
  • Size: 6.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for net_mesh-0.27.5.tar.gz
Algorithm Hash digest
SHA256 d195264931ed7d41c2a681c2ac1ff92fe6bf0acf92a2abf90b0bcb82a280f17d
MD5 6ce162ae43e3cce2fbd9db11f810c54b
BLAKE2b-256 22ec219dc8dadb667a8bdf96154b3c9e8fdd7deefa6ea6618ecd3711ce102ece

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 ba14a33fd607b7cab2fc28b4d8d8b31f76c2e4e4d3a98ef48402a579b0a6d011
MD5 7e6d4c8e3a8269252c091e9e4cacf1e8
BLAKE2b-256 06b8ba65d901f385d6c31821e8359e71ee29226c54276ec64584ef2911e5689c

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 86c40f00147071fc27c790cc7f4f001544d3070e96706eb83ed6b1ab9acc5ef9
MD5 228027d4eb1063ba38d1f9db3bdd5877
BLAKE2b-256 609f3d0f33d270e1bcbf2f10193cdeb7fec8ed88076d1b048fc64a765b380059

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp315-cp315t-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp315-cp315t-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 0204eee8ae6d3862e22b00c8b1d37dd4977051cebc9bf42172dab8a0925e9f1e
MD5 678ef19b1e94f20227c899c2b4392421
BLAKE2b-256 e912d6a5ae25aff0e55ed11d89e2a136e7428a60c029d54cc42b323aedc3d30d

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp315-cp315-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp315-cp315-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e007b949d8574a648e2d26fffeefa8c11346c6adf55d8cb1d4f2e6405dea6967
MD5 00df39fbcb566715a461037a9eca2fe9
BLAKE2b-256 7e21510654a2489a5a4e88ab063338b5a8b1d99d8ca0911f93ce427be80608b5

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp314-cp314t-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp314-cp314t-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 ec8a6b74edfb1dc3072ad618293cdd817dc7b0c17518edf2a8bc3889cc0f9a63
MD5 f8a4b80a5ef7bb761921f0e70b2c6eac
BLAKE2b-256 8d2322249ec9787d6f9b419a6c54fc71118a5bfcd414aefb6beb1569fed8f4f0

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp314-cp314t-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp314-cp314t-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 943bd08bfdad8b08cbd5ba81a647fcc35e0a7b5eb1f23f6a43a11c8c674f804b
MD5 f9098e499825944111a7645adccc68d6
BLAKE2b-256 3ff73531d655e31d158e29d49549a48c90feef1ca617a0a34293d5ecbdcee10a

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp314-cp314-win_arm64.whl.

File metadata

  • Download URL: net_mesh-0.27.5-cp314-cp314-win_arm64.whl
  • Upload date:
  • Size: 4.1 MB
  • Tags: CPython 3.14, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for net_mesh-0.27.5-cp314-cp314-win_arm64.whl
Algorithm Hash digest
SHA256 56c40d9d343da5306f1f220b0375a174d2ef53f266880571ae8b692312c9b8ab
MD5 32401803bd366b4d4897549977a8c86f
BLAKE2b-256 46d75b53cdc19bf5660905aae53654a80feec51a429fd7e3602a3c7ace93c7f2

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp314-cp314-win_amd64.whl.

File metadata

  • Download URL: net_mesh-0.27.5-cp314-cp314-win_amd64.whl
  • Upload date:
  • Size: 4.4 MB
  • Tags: CPython 3.14, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for net_mesh-0.27.5-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 a09abc6c93c63a26de3ef2b83a6919c6cbab7965a83b3224945a9526ac58522c
MD5 739b9fdb1183f4f5d88a40e2ff59ed2b
BLAKE2b-256 51ed1121287f7c90d977d47fed77d8d417cf5625fcbecca604303ea50672856d

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp314-cp314-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp314-cp314-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 3320b5f7a629f39b2ea645192fc6ca9b6c90646ac82da17d713d9879eb1417b1
MD5 5a2995c4417a97ec3faf9054ef18e57e
BLAKE2b-256 45564c16467859a830f8b43b255e253e01fa5785deec316a351178bb6655a2d6

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp314-cp314-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp314-cp314-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 a97ba429b27bc03fd073de09cca2e3c17e78a8c3c8006727b1cda9506d38d21e
MD5 65de3964481274d7eeeb407c61d4ba0b
BLAKE2b-256 1cdee90b1ecbe290da03bc8172ae4aba06b611480027d89ecfae496fbf4b70f5

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 144bbe1a5ca7f685685830c22f7b6043758e6793719d862b4a60c3066b093010
MD5 f6e155b55c8288acdd54606da1195d00
BLAKE2b-256 b1dfc2c970f0dd3364e9f4b8e5f1d02f9bf492103150fdd50a3a33eb85679b82

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp314-cp314-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp314-cp314-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 26907e644d1d7f02ee0ac17ef24ca95a721aaa618078cb773feba78c2eb2bdca
MD5 e07e5ee1d3d5b5e9e29d08438f0c8d8c
BLAKE2b-256 9538b9d67b2586fd8ed9bc83e6629562742e6d36b21069278e9a17c84fc75f5e

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp313-cp313-win_arm64.whl.

File metadata

  • Download URL: net_mesh-0.27.5-cp313-cp313-win_arm64.whl
  • Upload date:
  • Size: 4.1 MB
  • Tags: CPython 3.13, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for net_mesh-0.27.5-cp313-cp313-win_arm64.whl
Algorithm Hash digest
SHA256 0ba7d39d283782603c9a3a7572c781693e75791c2400dd6d0bc83c826dce21c2
MD5 ba7cadb88da6f111c15ae086c35f27c4
BLAKE2b-256 48e2f8b47cc92e5a885b1218c0badc5bcc568c2ffbfb2be26be3d5ff295a69cc

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: net_mesh-0.27.5-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 4.5 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for net_mesh-0.27.5-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 5731f3d0719c8c453e2a4f3b346b7268d41cde0ea0b5ebbc9776c23973195ecf
MD5 4cc0cb5b08d2ae00945eee73a6aec3af
BLAKE2b-256 746e62a4ba7f96b10eadd312829cb991f23ffd3f0fc43b7808e4168e307da5a3

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp313-cp313-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp313-cp313-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 926125211da2cdfff9db4b2b39ecbcc85c8584aece73e9e13af85f12b237fb26
MD5 c6fadc8342cdea51d0f86c4f74d24679
BLAKE2b-256 eeb4ece664956ff8fa89a1ef9a3105ff598354ff74d6f2a2662c217c6c588edd

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp313-cp313-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp313-cp313-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 ccf6ca57ccb233f69a34b9274d2c916ce8e923f9f220099c21b8b9ba9a3c1432
MD5 79bd65d6900ca570d4cd4288d7762988
BLAKE2b-256 f5a8c218636fcae7265ee6ddbb4047276f6c44834a455040dc741db991c42ccd

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b2cb97931c0a0f5d27d444a8f80a47a5ed2d595563caaae31de544ff4f4e8dca
MD5 422a70839fbd9ca37f44b8c4d257a36d
BLAKE2b-256 abd7e7bbea25983985f130e8cb4201b4df883c8aee6e80c5016ba677f904e67c

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp313-cp313-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 c734d593a31ef0c796b8d9b3513e3136159652ab97428795b022829e44bc76db
MD5 8ba1e1d3f7d90a227bf1e6f3e0a1fb70
BLAKE2b-256 702dcf5a32446f82c2c4df73d2a29843a07be435ac415b3b0c3c2a3d529e2af7

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp312-cp312-win_arm64.whl.

File metadata

  • Download URL: net_mesh-0.27.5-cp312-cp312-win_arm64.whl
  • Upload date:
  • Size: 4.1 MB
  • Tags: CPython 3.12, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for net_mesh-0.27.5-cp312-cp312-win_arm64.whl
Algorithm Hash digest
SHA256 f65e3e6e58a3a43eb6a7976b279059acbc09dd8f100809fdd350ddbddc25b09d
MD5 abacdaa0de16c2aee8868869f8943448
BLAKE2b-256 75ce66610085b4b332038d661b90a934e0a7d509804f985bb3d30a389e209b23

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: net_mesh-0.27.5-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 4.5 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for net_mesh-0.27.5-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 c64a0bf4797beb5287955023c6816b87326affd18d99cede740c4ad028566c34
MD5 37de245e30c5fdfdee28fa00d7429179
BLAKE2b-256 0f9979ca44aeaa55143156c75ed01bf8570dbe9709381a79c720173eb1c0d437

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp312-cp312-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp312-cp312-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 c8fbf3c2d2998b7e699f1c4287c7d554f21d74e1c807eb95ef08e64cde818ea9
MD5 ae4e24f4035adeb861fbecfbb3679391
BLAKE2b-256 a445bddffd4c631478ae43d7ee6871770be3dcba449ac53635dc2269b45f7937

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp312-cp312-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp312-cp312-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 37f75773304317ecc274d4505c50a869f95a3d7b5b3fcf8e24d56829c26ebb47
MD5 61f6b24fffa80650bdd9dac59b4bb5cc
BLAKE2b-256 a17c1630c91eacb2894896625f1a6259c025adba8d6ef42f6888ce92922a71b3

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 098332fb090eb696d8ec620869e6b282a094f583e66492a7e93214ca36181eb0
MD5 ce91cbbfddc0dd3819ca34b9a305de82
BLAKE2b-256 323aab6932b96035dc822ae6b32289fc82ced2ed2bb4fb6dc4e6cf17282acbdd

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp312-cp312-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 ba53a0457e796c05a59f8984d713004ed308d1ea6623341bff235f5f2af8979d
MD5 3dc2a209782edf507af6277ff0766ff7
BLAKE2b-256 673fbcbc47a8c68f94ac7e8a1cc8c64cf328b7e5eb00778f7ce7b515ea1ffdad

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp311-cp311-win_arm64.whl.

File metadata

  • Download URL: net_mesh-0.27.5-cp311-cp311-win_arm64.whl
  • Upload date:
  • Size: 4.2 MB
  • Tags: CPython 3.11, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for net_mesh-0.27.5-cp311-cp311-win_arm64.whl
Algorithm Hash digest
SHA256 37fbc1356f912b4c4722a4d7e560274f33c92a1caa6f6a3dc0a66a6cdcb82381
MD5 d49c547f148245863516b521f9e32463
BLAKE2b-256 54c298e8c1a0763dc44966b97cedca8d40cb2cc9b810b8a2de2de883590c9106

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: net_mesh-0.27.5-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 4.5 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for net_mesh-0.27.5-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 a0b2e6821dbecc83e88978dc0ee27a11de8b78dbf1608bd45655b2d4e28da692
MD5 0bc565ef6f54db8c2f16eb94b03e5941
BLAKE2b-256 4116d3b68a230eeb64b6936fd70585d2672691deccf23613f223adf0588ba4ea

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp311-cp311-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp311-cp311-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 637dc6c29a27ece236d8de0fab8ab6a9288a959b9a5df3b82f3ccdde85972313
MD5 a0356db6c30ee2480f9549da57c25286
BLAKE2b-256 d0cd2029f4de724e457383b54622e7f9725bf360942dd80475c2f9d6b49a6cd7

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp311-cp311-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp311-cp311-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 5ad34dceea2afab7ebf6dae46c72aee3576e3f7976981df6cf4243daaa7562e6
MD5 45a5f4d1fcb40ac6ac0c9b85b46532a1
BLAKE2b-256 c474c1a3f34c80429e091fe7f1e011db08c60828df0b51e3c407654760d2fc29

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 662316d60f515b047342cc212eb1eb8f1839806dfbb10a5adedcf32a28a484d5
MD5 b97a36d3990842964399db6254b6e01e
BLAKE2b-256 87f0b2cd1a841db2830cda06d308628608cb7a0c74dddf7bf4fe9143a9656736

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp311-cp311-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp311-cp311-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 8c0b117fd121cd6ce602fff2c46f49a5177792b3a4d3bd52ccb6593e63c64dfb
MD5 dfd6b5bccc997271afa94008f15e3f24
BLAKE2b-256 283e136b4f0f72b26408ba6c56fa4539b0e8b139d2769df9907b9b6e250e90bd

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: net_mesh-0.27.5-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 4.5 MB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for net_mesh-0.27.5-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 ad472b496aaf4b1bc1aec423593a596f3cace1fc4606811196b7193b4b1b3545
MD5 07023ef32907174049a8d24c09f3d4e2
BLAKE2b-256 e025b7a01bfba662265dee0c02017b4214303ae8af1f59966f07901244781055

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp310-cp310-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp310-cp310-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 18fceb7e5903a50283a641dd79de753565a90f24a3014f3f7c3f1c0b6ffc1da8
MD5 764833e581958a9271111d0cf2d27c8d
BLAKE2b-256 10cd85271c5edf0a45605f2612147212839284fa26d3be2354b484f34348b366

See more details on using hashes here.

File details

Details for the file net_mesh-0.27.5-cp310-cp310-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for net_mesh-0.27.5-cp310-cp310-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 b89356e3a7f499c71b045b42c57d5c7f54817591a925a9fe6547b2b242a3200b
MD5 adc2692786e36511cde71f616a7bc009
BLAKE2b-256 5faad62284ec82b2f26ea779104dfbfe7607cfffa1ea90df1493cc1a3b73ddda

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page