Grafting: Foreign Code Integration
Injecting the world’s code into your AI-first applications
What is Grafting?
Section titled “What is Grafting?”Grafting is Janus’ approach to foreign code integration. Unlike traditional “bindings” or “wrappers”, grafting allows you to seamlessly inject foreign libraries and runtimes directly into your Janus code while maintaining:
- Zero-cost abstraction - No runtime overhead
- Cryptographic security - Capability-based access control
- AI tooling integration - Self-documenting code with UTCP manuals
- Namespace hygiene - Contained under
std.graft.*
Result: You can use any Zig library or Python module as if it were native Janus code.
Native Zig Integration: The Home Ground
Section titled “Native Zig Integration: The Home Ground”Janus is built on Zig. You are not just grafting; you are extending the compiler substrate.
Current Rule
Section titled “Current Rule”Use Zig through explicit, auditable boundaries:
graft zig "path.zig" as namefor a Zig file that exports C-ABI functions.janus graft regenerateto materialize checked-in Janus wrappers undergraft/zig/.- Inline
graft zig do ... endonly for deliberate embedded-Zig escape hatches.
Ordinary long-lived Janus modules should prefer use std.* or generated wrappers. They should not grow broad use zig imports as a substitute for a native Janus surface.
Generated Zig Wrappers
Section titled “Generated Zig Wrappers”Run the generator whenever static graft zig declarations change:
graft zig "std/sys/linux/time.zig" as timeThen regenerate:
janus graft regeneratejanus graft regenerate --checkThe committed output lives in graft/zig/. Each wrapper has a generated-from-graft: header, calls the explicit graft alias, and tags exported Zig functions with {.profile: sovereign.} until finer capability inference lands.
Local File Grafting
Section titled “Local File Grafting”Import any .zig file from your project structure.
// Graft a local Zig filegraft zig "./vendor/math_utils.zig" as math
func compute() f64 do return math.fast_inverse_sqrt(16.0)endCross-Repository Roots: JANUS_ZIG_ROOTS
Section titled “Cross-Repository Roots: JANUS_ZIG_ROOTS”When the Zig file lives in a different repository, declare the external
root via the JANUS_ZIG_ROOTS environment variable — a colon-separated list
of absolute directories, tried in order after the local strategies
(absolute, source-relative, std/ under the Janus root, bare std/<name>
forms) and before the cwd fallback:
export JANUS_ZIG_ROOTS=/path/to/kernel/src:/path/to/other/repo// Resolves to /path/to/kernel/src/skills/propose.ziguse zig "skills/propose.zig"The first root containing the declared relative path wins. Resolution is deterministic — no symlinks and no dependence on the build’s working directory — so worktree-based and CI builds stay reproducible.
pub const Re-export Aliases (Gap 109c)
Section titled “pub const Re-export Aliases (Gap 109c)”A grafted Zig module may re-export a type from another file and use the alias as a graft type — no direct graft of the underlying file needed:
pub const Inner = struct { state: i64, ... };pub const ByteSlice = extern struct { ptr: [*]const u8, len: usize };
// mod.zig (the grafted file)pub const Foo = @import("defs.zig").Inner; // import re-exportpub const Bar = Foo; // same-file aliaspub const Bytes = @import("defs.zig").ByteSlice;pub export fn give_bytes() Bytes { ... }The alias behaves exactly like the underlying struct: extern fn
param/return types (including slice-shaped extern struct {ptr, len}
returns, which materialize as native Janus slices) and method resolution
(mod.Foo.method). @import following is limited to paths relative to the
grafted file — Zig-stdlib (std, std/...) and absolute imports stay
unresolvable and are skipped silently. Non-struct re-exports (constants,
functions) are not followed.
Zig-CC Methods and Comptime Generics: Trampoline Sidecars (Gap 109b)
Section titled “Zig-CC Methods and Comptime Generics: Trampoline Sidecars (Gap 109b)”Zig struct methods declared plain pub fn (Zig calling convention, not
pub export fn) and comptime-generic free functions
(pub fn max(comptime T: type, ...)) have no C-ABI symbol of their own.
When a Janus compilation unit actually calls one, the compiler now
synthesizes a C-ABI trampoline sidecar — a generated .zig with
pub export fn janus_zigcc_tramp_<...>(...) wrappers — compiles it through
the same zig build-obj pipeline stage as the grafted module, and rewrites
the call to the wrapper. No hand-written sidecar is needed for the covered
shapes:
// widget.zig (the grafted file)pub const Widget = struct { state: i64, pub fn ping(self: *Widget) i64 { return self.state; } // Zig-CC};pub fn gmax(comptime T: type, a: T, b: T) T { // generic return if (a > b) a else b;}graft zig "widget.zig" as probe
let w = probe.widget_make(10)let v = probe.Widget.ping(w) // trampoline: scalar/pointer signaturelet m = probe.gmax(7, 3) // trampoline: gmax(i64, 7, 3)Scope, honestly:
- Covered: Zig-CC methods whose params and return are C-ABI
scalars, sentinel strings, pointers, or optionals (the receiver is
passed explicitly, e.g.
probe.Widget.ping(w)); comptime-generic free functions of the exact shapef(comptime T: type, ...) -> T, instantiated from the first argument’s type (one wrapper per concrete type). - Still rejected (E2410): slice params/returns at the trampoline
boundary, error-union returns, comptime-generic methods, generics with
more than one comptime parameter or mixed parameter types. For those,
keep writing a
pub export fnsidecar wrapper by hand.
Quick Start: Injecting Zig Libraries
Section titled “Quick Start: Injecting Zig Libraries”Step 1: The Graft Declaration
Section titled “Step 1: The Graft Declaration”// Graft a checked-in bridge file.graft zig "std/sys/linux/time.zig" as time
func main() do let now = time.stp_now_ns() println(now)endThe foreign Zig module is available under the explicit alias. For public bridge surfaces, commit the generated wrapper and keep application code on the Janus side of the boundary.
Step 2: Capability-Based Security
Section titled “Step 2: Capability-Based Security”func secureProcess(path: string, ctx: Context) !Result do // Janus enforces capability checks automatically let data = std.fs.readFile(path, ctx.fs_read_capability); let result = my_zig_library.process(data); return result;endForeign code cannot access your filesystem without explicit capability grants.
Python Integration: Scripting Superpowers
Section titled “Python Integration: Scripting Superpowers”Foreign Blocks for Python
Section titled “Foreign Blocks for Python”// Create a Python runtime instanceforeign "python" as py do import numpy as np import matplotlib.pyplot as plt
def analyze_data(data): return { 'mean': np.mean(data), 'std': np.std(data), 'plot': plt.histogram(data) }end
// Use it seamlessly in Janusfunc processSensorData(raw_data: []f64, ctx: Context) !Analysis do using py := try std.graft.python.open(ctx, cap: ctx.foreign.python.ipc) do
// Convert Janus arrays to Python let py_array = py.call("numpy.array", [raw_data]);
// Call our custom Python function let analysis = try py.call("analyze_data", [py_array]);
// Extract results back to Janus types return Analysis{ mean: try analysis.get("mean").to_number(), std: try analysis.get("std").to_number(), histogram_data: try analysis.get("plot").to_array() }; endendYou just seamlessly called Python’s NumPy and Matplotlib from Janus code!
C Header Grafts: The Signature Contract
Section titled “C Header Grafts: The Signature Contract”Status: merged to
unstableata2780174(PR #34, Gaps 195, 201 and 202) on 2026-10-11; not yet in a tagged release. Every example below was compiled withjanus buildand run on Linux x86_64. The compiler prints the full source path in front of each location; the transcripts below shorten it to the file name.
graft c "header.h" as alias translates a C header and exposes its
functions under the alias (graft alias = c "header.h" is the same
declaration). The compiler calls them with exactly the parameter types and
order the header declares, and it refuses a call whose ABI it cannot
produce faithfully instead of compiling a wrong one.
Parameter names are optional
Section titled “Parameter names are optional”C prototypes may omit parameter names. Put this in prototype.h:
int abs(int);int strncmp(const char *, const char *right, unsigned long);graft c "prototype.h" as libc
func main() do print_int(libc.abs(-7)) print_int(libc.strncmp("sameA", "sameB", 4))endIt prints 7, then 0. Before PR #34 an unnamed parameter lost its call
argument and libc.abs(-7) printed an unrelated number. restrict pointer
parameters, named or unnamed, keep their pointer type as well.
Variadic functions are refused at the call (E2413)
Section titled “Variadic functions are refused at the call (E2413)”Janus does not implement the C variadic calling convention. Put this in
stdio_min.h:
int printf(const char *, ...);int puts(const char *);Save the program as variadic_call.jan next to the header:
graft c "stdio_min.h" as libc
func main() do libc.puts("fixed arity works") libc.printf("%d\n", 7)endThe build fails before any native code is emitted, and no binary is written:
variadic_call.jan:5:5: error: E2413: variadic C graft calls are not supported; expose a fixed C-ABI wrapper for 'libc.printf'Lowering error: UnsupportedForeignABIDelete the printf line and the same header builds: puts prints
fixed arity works. The variadic declaration is harmless; only calls to it
are refused. That covers direct calls, calls in defer and errdefer,
top-level calls, calls in imported modules, and even a call inside an
imported function that nothing uses. Variadic extern fn declarations that
use zig or graft zig read from a Zig file follow the same rule. Before
PR #34 such a call compiled and was lowered as if the function had a fixed
parameter list.
Write a fixed C-ABI adapter
Section titled “Write a fixed C-ABI adapter”Give the variadic function a fixed signature on the other side of the
border. A Zig adapter keeps the variadic call where the C convention is
implemented. Put this in fmt_adapter.zig:
extern fn printf(format: [*:0]const u8, ...) c_int;
pub export fn print_count(label: [*:0]const u8, count: c_long) callconv(.c) c_int { return printf("%s: %ld\n", label, count);}graft zig "fmt_adapter.zig" as fmt
func main() do println("before") fmt.print_count("files", 3) println("after")endIt prints before, files: 3, after. Janus calls print_count, whose
parameters are fixed; it never calls printf itself, so E2413 does not
apply.
Variadic function pointers are refused too
Section titled “Variadic function pointers are refused too”A function pointer can carry an ellipsis inside a parameter or return type.
Put this in logger.h:
void set_logger(int (*log)(const char *, ...));long (*get_sum(void))(long, ...);int abs(int);Save the program as fnptr_param.jan next to the header:
graft c "logger.h" as clog
func log(x: i64) -> i64 do return x end
func main() do clog.set_logger(log)endfnptr_param.jan:6:5: error: E2413: variadic C function pointers cannot cross the graft boundary; expose a fixed C-ABI adapter for 'clog.set_logger'Lowering error: UnsupportedForeignABIlet sum = clog.get_sum() fails the same way and names 'clog.get_sum'.
Janus func(...) values and indirect calls carry only fixed-arity calling
conventions, so a pointer to a variadic function would be called with the
wrong ABI. Write a fixed adapter that forwards to the variadic function, as
above. Function pointers with fixed parameter lists remain usable, and the
unused declarations do not affect the rest of the header: clog.abs(-7)
builds and prints 7.
- The contract applies to
graft c,use zigandgraft zig. - Aggregate (struct-by-value) ABI support is unchanged by this work.
- A grafted function cannot be passed by name as a
funcvalue:apply(libc.abs, -7)fails witherror: no exported symbol in imported module 'abs'. - The C-graft subprocess launcher supports Linux hosts; other hosts fail with
UnsupportedGraftHost. - Native regressions:
./scripts/zb test-c-header-discoveryin the compiler repository.
C Integration: The Universal Glue
Section titled “C Integration: The Universal Glue”Status: the forms in this section (
graft libc = c "libc.so.6",foreign "c" as name { ... }) are design sketches, not verified compiler paths. The verified C form isgraft c "header.h" as alias, optionally withlink "library"; see C Header Grafts: The Signature Contract.
Direct Shared Object Grafting
Section titled “Direct Shared Object Grafting”Janus treats C as the “lingua franca” of the system. You can graft shared objects (.so, .dll, .dylib) directly.
// Graft a standard C librarygraft libc = c "libc.so.6";
func getCurrentTime() i64 do // Call C functions directly with zero overhead return libc.time(null);endHeader Definitions
Section titled “Header Definitions”For complex C libraries, you can provide inline C definitions to help the Janus compiler understand types.
foreign "c" as raylib { #include "raylib.h" // Janus automatically parses the header AST}
func drawGame() do raylib.InitWindow(800, 600, "Janus Window"); while !raylib.WindowShouldClose() do raylib.BeginDrawing(); raylib.ClearBackground(raylib.RAYWHITE); raylib.DrawText("Hello C-Realms!", 190, 200, 20, raylib.LIGHTGRAY); raylib.EndDrawing(); end raylib.CloseWindow();endRust Integration: Oxidized Safety
Section titled “Rust Integration: Oxidized Safety”Cargo Crate Grafting
Section titled “Cargo Crate Grafting”Janus can drink directly from the Cargo stream. This requires cargo to be available in the build environment.
// Graft a Rust crate directly from a git repograft rocket = cargo "https://github.com/rwf2/Rocket";
// Or specify a crate version (uses crates.io)graft serde = cargo "serde:1.0";The Safety Bridge
Section titled “The Safety Bridge”Janus respects Rust’s safety guarantees.
// Using the grafted Rust libraryfunc handleRequest(req: Request) !Response do // Rust's Results are mapped to Janus error unions automatically let processed = try rocket.process(req); return processed;endNote: The first time you build, Janus will invoke Cargo to compile the grafted crate as a static library, then link it.
Julia Integration: Scientific Sovereignty
Section titled “Julia Integration: Scientific Sovereignty”For high-performance numerical computing, nothing beats Julia.
Foreign Blocks for Julia
Section titled “Foreign Blocks for Julia”foreign "julia" as flux do using Flux using LinearAlgebra
function train_model(x_data, y_data) model = Chain(Dense(10, 5, relu), Dense(5, 2)) loss(x, y) = Flux.mse(model(x), y) Flux.train!(loss, Flux.params(model), [(x_data, y_data)], ADAM()) return model endendTensor Bridging
Section titled “Tensor Bridging”Janus Arrays ([]f64) map directly to Julia Arrays with zero checking.
func runTraining() !Model do // Capability: Requires 'julia.embed' capability using jl := try std.graft.julia.open(ctx, cap: ctx.foreign.julia.embed) do let x = std.tensor.random([10, 100]); let y = std.tensor.random([2, 100]);
// Pass Janus tensors to Julia // Memory is shared, not copied! let model = jl.call("train_model", [x, y]);
return model; endendComplete Examples
Section titled “Complete Examples”Example 1: High-Performance Math
Section titled “Example 1: High-Performance Math”// Graft BLAS library for linear algebragraft zig "vendor/blas_bridge.zig" as blas
func matrixMultiply(a: Matrix, b: Matrix) Matrix do // Direct call to optimized BLAS return blas.gemm(a, b, 1.0, 0.0)endExample 2: Web Scraping with Python
Section titled “Example 2: Web Scraping with Python”foreign "python" as scraper do import requests from bs4 import BeautifulSoup
def extract_titles(url): response = requests.get(url) soup = BeautifulSoup(response.text, 'html.parser') return [h1.text for h1 in soup.find_all('h1')]end
func scrapeHeadlines(url: string, ctx: Context) ![]string do using scraper := try std.graft.python.open(ctx, cap: ctx.foreign.python.ipc) do let titles = try scraper.call("extract_titles", [url]); return titles.to_array(); // Automatic conversion endendExample 3: Cryptographic Operations
Section titled “Example 3: Cryptographic Operations”// Graft a crypto librarygraft zig "vendor/crypto_bridge.zig" as crypto
// Janus handles capability verification automaticallyfunc encryptData(data: []u8, key: Key) ![]u8 do return crypto.aead_encrypt(data, key, "JanusData")endThe Janus Advantage
Section titled “The Janus Advantage”Performance
Section titled “Performance”- Zero-copy data transfer where possible
- Native compilation of grafted Zig code
- Profile optimization (:core vs :sovereign performance modes)
Security
Section titled “Security”- Capability tokens required for sensitive operations
- Sandboxing prevents foreign code escape
- Audit trails track all foreign function calls
- Memory Safety: Rust grafts retain borrow checker guarantees at the boundary
Scientific Sovereignty
Section titled “Scientific Sovereignty”- Julia & Python: Direct memory mapping for tensors (Zero-Copy)
- Unified Logic: One language (Janus) to orchestrate C, Rust, Python, and Julia
AI Integration
Section titled “AI Integration”// UTCP manuals make foreign code discoverablefunc discoverCapabilities() ![]UTCPManual do // AI tools can explore all grafted capabilities return std.graft.enumerateAll();endGrafting Patterns
Section titled “Grafting Patterns”Pattern 1: Zig Library Integration
Section titled “Pattern 1: Zig Library Integration”// For algorithm-heavy codegraft zig "path/to/zig_algorithm_bridge.zig" as algorithm
func processLargeDataset(data: Dataset) Result do // Use optimized parallel algorithms return algorithm.parallel_process(data)endPattern 2: Python Ecosystem Access
Section titled “Pattern 2: Python Ecosystem Access”foreign "python" as ecosystem do import scikit-learn import pandas as pd # ... ML pipeline code ...end
func trainModel(training_data: Dataset) Model do using ecosystem := std.graft.python.open(...) do let df = ecosystem.call("pandas.DataFrame", [training_data]); return ecosystem.call("train_ml_model", [df]); endendPattern 3: Legacy System Integration
Section titled “Pattern 3: Legacy System Integration”// Connect to existing C librariesgraft legacy = c "legacy_system.so";
func migrateLegacyData() MigrationResult do let raw_data = legacy.fetch_records(); return transformToModernFormat(raw_data);endAdvanced Features
Section titled “Advanced Features”Resource Management
Section titled “Resource Management”// Automatic cleanupusing py := std.graft.python.open(ctx, cap: ctx.foreign.python.ipc) do let result = py.call("process_data", [data]); // py.close() called automatically hereendError Handling
Section titled “Error Handling”let result = py.call("risky_operation", [args]) or do |err| // Handle Python exceptions in Janus match err.kind { .ForeignError => log.warn("Foreign code error: {err.message}"); .CapabilityMissing => return Error.InsufficientPrivileges; .Timeout => return Error.OperationTimeout; }endForeign Data Types
Section titled “Foreign Data Types”// Automatic marshallinglet python_dict = py.eval("{'key': 'value'}");let janus_table = python_dict.to_table(); // Converts Python dict to Janus table
// Zero-copy for arrays (where supported)let numpy_array = py.call("numpy.linspace", [0, 100, 50]);let janus_array = numpy_array.to_ndarray("f64"); // Direct memory mappingConfiguration and Profiles
Section titled “Configuration and Profiles”Profile-Specific Grafting
Section titled “Profile-Specific Grafting”// :core profile - IPC onlyusing py := std.graft.python.open(ctx, cap: ctx.foreign.python.ipc);
// :sovereign profile - Embed Python VMusing py := std.graft.python.open(ctx, cap: ctx.foreign.python.embed);Policy-Based Governance
Section titled “Policy-Based Governance”foreign { python { deterministic = false // Allow non-deterministic ops in trusted contexts timeout_ms = 5000 allowed_modules = ["numpy", "scipy", "matplotlib"] }}Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”-
Capability Missing
// Add to function parametersfunc myFunc(ctx: Context) do // Context provides capabilitieslet py = std.graft.python.open(ctx, cap: ctx.foreign.python.ipc);end -
Timeout Errors
let result = py.call("long_running", args, timeout_ms: 30000); -
Type Conversion Issues
// Explicit conversionslet py_obj = py.eval("some_python_expression");let janus_value = py_obj.to_number() or do return Error.ConversionFailed; end
What’s Next?
Section titled “What’s Next?”Grafting is just getting started:
- More Languages: Rust, Lua, JavaScript, R integration
- Performance: Direct memory mapping for tensor operations
- AI Integration: Automatic code generation from UTCP manuals
- Cloud: Distributed grafting across Janus clusters
With Janus grafting, your code can leverage the entire world’s software ecosystem while maintaining security, performance, and AI-first design principles.