Skip to content

Import System

Janus provides a clear, hierarchical import system that distinguishes between native Janus modules, Zig libraries, and C libraries. This guide covers all three mechanisms and explains when to use each.

MechanismSyntaxPurposeRuntime Cost
Nativeuse module.nameImport Janus modulesZero
Zig Bridgeuse zig "path.zig"Import an explicit Zig bridge module or generated wrapperZero
C Graftgraft c "header.h"Import C librariesFFI boundary

The use statement is the Janus-native module import mechanism. It creates a namespace binding named after the last path component.

// Import a module — binds the name "http" to the module's exports
use std.net.http
// Use the imported module
const response = http.get("https://api.example.com/data")

The compiler resolves module paths by:

  1. Joining identifiers with / (e.g., std.net.http → std/net/http.jan)
  2. Appending .jan extension
  3. Searching relative to the source file’s directory
  4. Falling back to Janus stdlib root

When you need a different name or want to avoid conflicts:

// Rename on import
use std.crypto.blake3 as hash
use std.io.file as fs
// Use the alias
const digest = hash.digest(data)
const content = fs.read("config.txt")

An import alias is a local name for a resolved module. The module contract keeps functions in distinct files separate even when their filenames and function names match. Importing one resolved file through several aliases or through a shared dependency must continue to refer to that same module.

Development caveat, September 14, 2026: the compiler’s internal function names used only the filename stem, which could conflate distinct files such as context/state.jan and matchmaker/state.jan. Changing their local aliases did not prevent that collision. A correction that uses a per-compilation module identity passes focused native regressions in local commit 1b7725fa. The subsequent scalar-type corrections are also locally verified. A rebuilt Graf executable runs, but its native Git consumer fixture fails; consumer qualification remains pending. See the module identity development note for the affected scope and validation status.

Modules can re-export other modules to create convenience indexes:

// std/std.jan — Standard Library Index
pub use std.core as core
pub use std.io as io
pub use std.net.http as http
// Users can now:
// use std.http // Instead of use std.net.http

Use use zig only when you are crossing an intentional Janus/Zig boundary. That usually means one of these:

  • an approved bridge module under std/bridge
  • a local project bridge file
  • a compiler-generated wrapper

Normal application .jan files should prefer Janus modules such as use std.crypto.blake3.

// Explicit bridge modules
use zig "std/sys/linux/process.zig"
use zig "std/bridge/mem_raw_bridge.zig"
// Local project bridges are also valid
use zig "bridge/net_bridge.zig"

The term “graft” reflects the Janus philosophy: foreign code is explicitly marked, contained, and can be replaced. Unlike opaque FFI bindings, grafted code:

  • Is visible in your source (explicit use zig or graft c)
  • Is typed (Zig/C types map to Janus types at compile time)
  • Is replaceable (swap the graft without changing call sites)

For C libraries, use the graft c syntax with explicit aliasing and optional linking.

// Import a C header with an alias
graft c "oqs/oqs.h" as oqs
graft c "argon2.h" as argon2
// Use via the alias
const rc = oqs.OQS_SIG_ml_dsa_65_verify(...)
// Specify the library to link against
graft c "oqs/oqs.h" as oqs link "oqs"
graft c "argon2.h" as argon2 link "argon2"
graft c "sodium.h" as sodium link "sodium"

C code integration follows three principles:

  1. Explicit: The graft c keyword makes foreign code obvious
  2. Contained: All FFI calls go through the named alias
  3. Replaceable: Swap implementations without changing consuming code

Since a2780174 on unstable (PR #34, 2026-10-11, not yet in a tagged release), grafted C signatures follow a fail-closed contract. It applies to graft c headers and to extern declarations read through use zig and graft zig:

DeclarationResult
int abs(int); (unnamed parameters)Called like int abs(int value);; type and position preserved
restrict pointer parametersKeep their pointer type
int printf(const char *, ...);Declaration accepted; every call fails with E2413
A variadic function pointer in a parameter or return typeCalls fail with E2413
Fixed-signature functions and function pointersUsable as before

Call a variadic C function through a fixed C-ABI adapter instead. The grafting reference shows the diagnostics and a working adapter.

// ❌ WRONG — Don't mix mechanisms
const std = @import("std") // Zig syntax, not Janus
import * as http from "http" // JavaScript syntax
#include <stdio.h> // C preprocessor syntax
// ✅ CORRECT — Janus-native imports
use std.io
use zig "std/bridge/http_bridge.zig"
graft c "stdio.h" as c_stdio
// Standard library
use std.core
use std.io.file
use std.net.http
use std.crypto.blake3
// Local project modules
use myapp.models
use myapp.controllers.auth
// Approved bridge modules
use zig "std/sys/linux/process.zig"
use zig "std/sys/linux/http.zig"
// Local Zig files (relative to source file)
use zig "../native/optimized.zig"
use zig "bridge/wasm.zig"
// System headers
graft c "stdio.h" as c_stdio
graft c "stdlib.h" as c_stdlib
// Local headers
graft c "../vendor/custom.h" as vendor
graft c "native/specialized.h" as native

Use native Janus modules when available:

// ✅ Good — Native Janus
use std.crypto.blake3
// ⚠️ Only if needed — explicit bridge module
use zig "std/sys/linux/time.zig"
// ✅ Good — Clear intent
use std.crypto.blake3 as hasher
use std.io.file as fs
// ❌ Avoid — Cryptic aliases
use std.crypto.blake3 as b3
use std.io.file as f
// Standard library imports
use std.core
use std.io
use std.net.http
// Explicit bridge imports
use zig "std/sys/linux/process.zig"
// C library imports
graft c "oqs/oqs.h" as oqs
// Local project imports
use myapp.config
use myapp.models
//! TLS implementation using OpenSSL via graft
graft c "openssl/ssl.h" as ssl link "ssl"
graft c "openssl/err.h" as ssl_err link "ssl"
/// Initialize SSL context
/// Requires: libssl-dev installed on system
pub func initContext() ssl.SSL_CTX {
// ...
}

Prior to version 2026.4.3, some Janus files incorrectly used Zig’s @import syntax. This has been deprecated and removed. Migrations should prefer Janus module paths first and only introduce use zig when the target is an explicit bridge module.

Old (Zig syntax)New (Janus syntax)
const std = @import("std")use std
const core = @import("core.jan")use std.core as core
const hash = @import("crypto/blake3.jan")use std.crypto.blake3 as hash
const local = @import("../local.jan")use local (or appropriate module path)
  1. Language Identity: Janus is distinct from Zig; mixing syntax creates confusion
  2. Future-Proofing: The @ character is reserved for future inline documentation (e.g., @param, @deprecated)
  3. Consistency: A single, clear import mechanism for each use case