architecture Intermediate 10 min read

How Local Compilation Works

The full path from import through compilation to native execution in pyvorin-native 1.0.9 — where the cache lives, what the fallback does, and exactly what does and does not leave the machine.

Published Aug 12, 2026

Pyvorin Native compiles your Python to machine code on your own hardware, inside your own process. The compile path contains zero network code — not a single urllib, urlopen or requests call anywhere in the compiler, the router, the code generator or the C backend. That is not a marketing claim; it is what a line-by-line source review of the 1.0.9 tree found, corroborated by a string sweep of every installed binary. Only two kinds of traffic ever leave a machine running pyvorin-native: licence checks and telemetry. Nothing about your source code is in either one.

This page traces the whole path: what happens when you import pyvorin, how a function gets from Python source to native code, where the results are cached on disk, what the fallback path does when compilation is not possible, and the complete list of outbound network calls with their payloads. Where a design decision involves a trade-off — and several do — we say so and give our reasoning, because "local compilation" is a claim you should be able to audit, not a vibe you should trust.

Import: integrity check, then cache directories

Importing pyvorin does two things before you have called a single API. First, pyvorin.licensing.core runs pyvorin._integrity.verify() once per process. This SHA256-hashes every compiled .so in the package against a table of expected hashes baked into _integrity_data.py — a generated file carrying the same build watermark as the rest of the package. It takes about five milliseconds. If any hash mismatches, the import raises a RuntimeError naming tampering, and the process stops rather than run code of unknown provenance. Setting PYVORIN_DEV_MODE=1 skips the check; that escape hatch exists for the development tree, not for production use.

Second, the import creates cache directories. The disk compile cache lives inside the installation tree at <site-packages>/.pyvorin_cache/disk_compile — not in your home directory, a detail that surprises people and matters for read-only container deployments, which we will come back to. Importing also creates ~/.pyvorin_cache/columnar_kernels/ even when no licence activity has occurred; this was observed in a clean home directory during verification and is worth knowing if you plan to claim "no writes until activated" in an audit. A separate user cache directory, ~/.cache/pyvorin (XDG-aware, %LOCALAPPDATA%\Pyvorin on Windows), is defined for other runtime state.

The compile path, stage by stage

Compilation is in-process. The package docstring describes itself as an "In-Process Python Module … without HTTP overhead", and the architecture matches: NativeCompiler lowers Python to LLVM IR via llvmlite and links against local runtime libraries that ship inside the wheel. There is no compile server, no worker queue, no job ID, no artefact download. When you ask for a function to be compiled, the work happens between one bytecode boundary and the next, on the CPU you are already running on. This matters for two practical reasons: compilation latency is measured in milliseconds rather than network round-trips, and the confidentiality of your source is a structural property — there is simply no code path that could exfiltrate it, rather than a policy that promises not to.

The path from source to native code runs through three broad stages.

Frontend classification. The frontend assigns every import a capability tier — compile-safe, runtime-callable, compatibility-only or unsupported. Unsupported imports raise an ImportPolicyError at the frontend, before any compilation is attempted, so an unsupported dependency fails fast and loudly rather than silently poisoning a compile job. Unsupported constructs inside a function are handled differently, as we will see under fallback.

Routing. A unified router sends every function to NativeCompiler first in auto mode. The legacy router still exists in the tree but is deprecated: importing it emits a DeprecationWarning, and setting PYVORIN_BACKEND=legacy raises a RuntimeError stating the old stack was removed from production paths. The only meaningful backend choices are native and auto.

Code generation. The compiler lowers the function through LLVM (via llvmlite) to a native .so, linking local runtime libraries for list and array support. Optional stages sit on top: SIMD vectorisation, parallel reduction loops and profile-guided optimisation. Each stage can be disabled independently from the CLI (--no-vectorize, --no-parallel, --no-pgo), which is not just a debugging convenience: it lets you attribute a measured speedup to a specific stage on your own hardware rather than taking the aggregate on faith. The @kernel decorator marks a function with a _nexus_kernel attribute — a one-line marker consumed by the kernel-substitution pipeline, not a magic keyword that changes semantics. If you do not use it, nothing is lost; the router discovers hot, compilable functions on its own.

The result is recorded honestly: every run exposes backend_used, one of pyvorin-native-llvm, pyvorin-cpython-fallback or pyvorin-invalid. You never have to guess which engine executed your function.

The disk cache

Compiled output is cached by DiskCompileCache in <site-packages>/.pyvorin_cache/disk_compile. Each entry is a compiled .so plus a cache_index.json, keyed by the SHA256 of the source, the function name, the compile options and the modification times of the runtime and compiler files. Any change to the source or the toolchain invalidates the entry, which is the correct conservative behaviour: a cache hit must mean "this exact code compiled with this exact toolchain", not "something similar". Entries carry checksums that are validated on read, and the cache is bounded by an LRU eviction policy at 500 entries.

The location inside the install tree is a deliberate engineering trade-off worth understanding. A cache co-located with the package is trivial to find, share between virtual environments pointing at the same interpreter, and clean up with the package. Its cost is that the install directory must be writable by the running process. In containers and hardened hosts that mount site-packages read-only, the first compile will fail to persist its cache — execution still works, but every process pays the full compile cost again. Our recommendation for such deployments is to plan for this explicitly rather than discover it under load: either grant a writable cache path or accept the recompilation cost in your warm-up budget. A cache under the user's home directory would dodge the permission problem entirely but would fragment silently across users and containers; there is no free choice here, only two different bills.

On a cache hit the saved .so is still not trusted blindly: each entry's checksum is re-validated when read, so a corrupted cache file is detected and treated as a miss rather than executed. That choice costs a hash read per lookup and buys immunity against the classic failure of disk caches — returning garbage confidently. The 500-entry LRU bound caps disk growth in long-lived environments where thousands of distinct functions might be compiled over weeks; the least recently used entries are evicted silently, and a recompile simply repopulates them.

When compilation is not possible: the fallback

The compiler's contract is correctness first. When a function contains constructs the native path cannot handle, the router does not crash the program and does not emit a best-effort guess. It marks the function and executes an honest CPython fallback — the original Python source, lazily compiled by the ordinary interpreter — with correctness preserved. The fallback events are recorded by a diagnostics logger, so the decision is auditable after the fact rather than invisible.

Guard failures at runtime follow the same discipline. If an assumption baked into the native code stops holding — a type changes, a shape shifts — execution diverts to the lazily-compiled Python fallback of the original source mid-run. The program continues correctly, slower. Strict mode exists at the frontend for those who would rather pre-reject unsupported code than ever touch the fallback path, but the default is transparent degradation, and we think that default is right for production: a surprise slow-down is recoverable; a surprise crash in a batch job at 3am is not.

Five workloads in the standard benchmark suite took this path rather than complete through the compiler — a moving average, a rolling IoT average, a k-nearest-neighbours loop, a matrix multiplication and a prime sieve. In each case the suite continued and produced correct results. That is the fallback working as designed, and it is documented in detail on the unsupported and fallback page.

What leaves the machine

The complete outbound surface of pyvorin-native 1.0.9 fits in a short table. This is the entire list — there is no update check, no version ping, no error-reporting endpoint:

EndpointWhen calledPayload
POST api.pyvorin.com/api/v1/licenses/checkpyvorin activate, plus periodic revalidation from the licence gateLicence key and device fingerprint
POST api.pyvorin.com/v1/usage/events/bulkTelemetry flush: 50 events, 300 seconds, or process exitEvent metadata envelope: event names, timings, privacy level, locally computed abuse score
POST api.pyvorin.com/api/auth/token / revokeOnly login / logout in the product CLIAccount credentials / token

Neither licensing nor telemetry carries source code, file paths, variable names or environment details. The licence payload is exactly {"license_key": …, "device_fingerprint": …} — the fingerprint being a truncated SHA256 of the hostname, username and machine ID, used to bind the lease to one device. The telemetry envelope carries event metadata: the event name (such as compile or execute), a timing, the privacy level and an abuse score computed locally from counts such as validation failures and tamper events. Compile, execute and feature events are sampled at 1%; activation, validation failure, tamper and revocation events are always recorded. Two environment variables, PYVORIN_LICENSE_URL and PYVORIN_TELEMETRY_URL, can redirect these destinations, for example to a logging proxy for inspection.

Three findings from verification deserve emphasis because each kills a common assumption. There is no update-check call anywhere in the package — nothing phones home to ask whether a newer version exists. The client contacts api.pyvorin.com only; the license.pyvorin.com and telemetry.pyvorin.com vhosts exist on the infrastructure but are never called by the 1.0.9 native client. And telemetry has no environment-variable or config-file opt-out: privacy can be set to minimal or disabled only through an in-process API call. That last point is a genuine limitation, and we would rather you learned it here than from a packet capture.

Telemetry itself deserves a precise description, because "we collect telemetry" spans everything from innocuous to alarming. The transport is batched and fire-and-forget: a ring buffer of at most 1,000 events, flushed at 50 events, 300 seconds or process exit, over a 5-second timeout, with failures silently ignored and no background thread. The events are metadata, not content: compile carries a duration, execute a duration, feature_used a feature name — and those three are sampled at 1%. The always-recorded events are security-relevant ones: activation, validation_failed, tamper_detected and revoked. A locally computed abuse score rides with each batch. There is no stack trace capture, no variable inspection, no source. For most teams this is the profile of an ordinary product-analytics beacon; for regulated environments, the in-process set_privacy call is the control to reach for, and network policy on the telemetry endpoint is the backstop.

How this was verified, and the honest boundary

The claims above come from two kinds of evidence, both gathered on the installed 1.0.9 artefact. Static review found no network primitives anywhere in the compile path. Runtime capture went further: the verifier monkeypatched urllib.request.urlopen inside a throwaway home directory and exercised activation, compilation and gating, recording every attempted request. The only requests observed were the licence check and the telemetry flush, matching the table above.

One boundary must be stated plainly. These guarantees apply to pyvorin-native, the package this page describes. A sibling product, pyvorin-thin, is a different package with a different architecture: it submits source to a remote compile endpoint on api.pyvorin.com. Server-side download endpoints for compiled artefacts also exist on the platform API, used by other clients. Neither touches the native package. Any blanket statement that "Pyvorin never sends source anywhere" is false as scoped across the product family; the true, verifiable statement is the narrower one — pyvorin-native's compile path has zero network code, and no source code leaves the machine running it.

Where to go next

Last reviewed 30 July 2026 against pyvorin-native 1.0.9, build watermark BW-2e0a2cbabc684a649b3b092cf2039fcd. Network behaviour was captured by intercepting urllib.request.urlopen in a throwaway home directory; no real external calls were made during verification.