OS, files & hardware
Read and write files, detect the operating system, query environment variables, and reach specialized hardware through the stdlib, FFI, or language bridge.
Run your project
Any program that uses import must be built from the project directory, not as a lone file:
cd myapp
nyra run .
nyra run . for projects
nyra run main.ny compiles a single file and does not load imports. Multi-file apps and stdlib imports need nyra run . or nyra build .. See Imports guide.
Import stdlib modules with short paths from any project depth:
import "stdlib/fs.ny"
import "stdlib/os.ny"import "stdlib/fs.ny"
import "stdlib/os.ny"Filesystem
import "stdlib/fs.ny" (or stdlib/fs/mod.ny) gives you the filesystem API. API reference: Stdlib → fs.
| Function | Description |
|---|---|
write_file(path, content) | Create or overwrite a file |
read_file(path) | Read entire file into a string (owned, auto-dropped) |
append_file(path, content) | Append to an existing file |
exists(path) | Returns 1 if path exists, else 0 |
create_dir(path) | Create a directory |
remove_file(path) / remove_dir(path) | Delete file or directory |
import "stdlib/fs.ny"
fn main() {
write_file("hello.txt", "Written from Nyra\n")
if exists("hello.txt") == 1 {
let content = read_file("hello.txt")
print(content)
}
}import "stdlib/fs.ny"
fn main() -> void {
write_file("hello.txt", "Written from Nyra\n")
if exists("hello.txt") == 1 {
let content = read_file("hello.txt")
print(content)
}
}Example in repo: examples/projects/read_file/main.ny
Operating system
import "stdlib/os.ny" aggregates platform detection, environment, battery, POSIX-style I/O, and syscall helpers (macOS, Linux, Windows). See Stdlib → os.
Platform detection
| Function | Returns |
|---|---|
platform_name() | "darwin", "linux", or "windows" |
is_darwin() / is_linux() / is_windows() | bool platform checks |
platform_id() | Numeric constant (PLATFORM_LINUX, …) |
page_size() | OS memory page size in bytes |
Process & environment
| Function | Description |
|---|---|
os_getenv(name) | Read an environment variable. Use os_getenv — not getenv (libc name collision at link time). |
os_getpid() | Current process ID |
os_exit(code) | Terminate the process |
battery_percent() | 0–100 on laptops, or -1 if unavailable |
import "stdlib/os.ny"
fn main() {
print(platform_name())
print(os_getenv("HOME"))
print(os_getpid())
if is_darwin() {
print("Running on macOS")
} else if is_linux() {
print("Running on Linux")
}
}import "stdlib/os.ny"
fn main() -> void {
print(platform_name())
print(os_getenv("HOME"))
print(os_getpid())
if is_darwin() {
print("Running on macOS")
} else if is_linux() {
print("Running on Linux")
}
}Run examples from the repo root:
nyra run examples/os/platform/main.ny
nyra run examples/os/battery/main.ny
Run external commands
To spawn another program (like Rust’s Command), use stdlib/process.ny — auto-prelude symbols Command_new, .arg, .run. Returns the child exit code; stdout/stderr inherit your terminal. For shell strings (pipes, globs), run /bin/sh -c "…".
fn main() {
let code = Command_new("ls").arg("-la").run()
print(code)
let sh = Command_new("/bin/sh").arg("-c").arg("uname -a").run()
print(sh)
}fn main() -> void {
let code = Command_new("ls").arg("-la").run()
print(code)
let sh = Command_new("/bin/sh").arg("-c").arg("uname -a").run()
print(sh)
}Full reference: stdlib → Subprocess & commands · example examples/process_command.ny. To capture stdout as a string, use stdlib/bridge/mod.ny (language bridge).
User input
input("prompt") is a builtin — no import required. It reads a line from stdin and returns a string. Full I/O builtins: stdlib → Built-ins → I/O.
fn main() {
let name = "Ada" // in apps: let name = input("Enter your name)
print(`Hello, ${name}!`)
}fn main() -> void {
let name = "Ada" // in apps: let name = input("Enter your name)
print(`Hello, ${name}!`)
}Low-level: syscalls & assembly
For kernel-style or driver code, use the POSIX wrappers and raw syscall layer:
| API | Module | Purpose |
|---|---|---|
os_read, os_write, os_close | stdlib/os/unistd.ny | File-descriptor I/O |
os_syscall6(num, a0..a5) | stdlib/os/unistd.ny | Raw syscall with OS-specific number |
SYS_* constants | syscall_linux.ny / syscall_darwin.ny | Syscall numbers for os_syscall6 |
unsafe { asm "nop" } | Nyra language | Inline assembly template |
cpu_nop(), cpu_pause() | stdlib/os/asm.ny | Prebuilt asm helpers |
import "stdlib/os.ny"
fn main() {
unsafe {
asm "nop"
}
cpu_nop()
print(os_getpid())
}import "stdlib/os.ny"
fn main() -> void {
unsafe {
asm "nop"
}
cpu_nop()
print(os_getpid())
}For no_std / embedded / MMIO register access, see Memory → unsafe and stdlib/core/mem.ny (volatile_load_i32, *T pointers). Example: examples/os/asm/main.ny.
Hardware topology (stdlib/os)
Since import "stdlib/os.ny" also pulls hardware helpers:
| Module | Key APIs |
|---|---|
cpu.ny | cpu_brand(), physical/logical cores, cache line, cpu_has_avx2() |
memory.ny | mem_map_anonymous, mem_unmap, hw_page_size(), dma_available() |
storage.ny | disk_fs_type, disk_total_bytes, disk_free_bytes |
netif.ny | net_interface_count, net_interface_mac, net_interface_is_up |
display.ny | display_width, display_height, display_refresh_hz, brightness |
power.ny | power_on_ac(), power_cpu_temp_centi_c() + battery_percent() |
syscall_windows.ny | Windows Nt / VirtualAlloc constants |
import "stdlib/os.ny"
fn main() {
print(cpu_brand())
print(cpu_logical_cores())
print(disk_fs_type("/"))
print(net_interface_mac(0))
print(display_width())
}import "stdlib/os.ny"
fn main() -> void {
print(cpu_brand())
print(cpu_logical_cores())
print(disk_fs_type("/"))
print(net_interface_mac(0))
print(display_width())
}Example: nyra run examples/os/hw/main.ny
Example: nyra run examples/os/hw/main.ny
Advanced CPU control
Modules: affinity.ny, clocks.ny
CPU affinity
Pin the current OS thread to a logical core — useful for real-time loops and reducing migration overhead.
import "stdlib/os.ny"
fn main() {
let cores = cpu_logical_cores()
print(cores)
let ok = affinity_set_thread_cpu(0) // 0 on success
print(ok)
print(affinity_get_thread_cpu())
}import "stdlib/os.ny"
fn main() -> void {
let cores = cpu_logical_cores()
print(cores)
let ok = affinity_set_thread_cpu(0) // 0 on success
print(ok)
print(affinity_get_thread_cpu())
}| Function | Notes |
|---|---|
affinity_set_thread_cpu(n) | Linux pthread_setaffinity_np, macOS thread policy, Windows SetThreadAffinityMask |
affinity_get_thread_cpu() | Returns first pinned core or current CPU; -1 if unknown |
High-resolution clocks
Cycle counters for micro-benchmarks and profilers. Returns i64.
import "stdlib/os.ny"
fn main() {
let t0 = clock_monotonic_ns()
// ... work ...
let elapsed = clock_elapsed_ns(t0)
let cycles = clock_rdtsc() // RDTSC on x86_64, cntvct on ARM64
}import "stdlib/os.ny"
fn main() -> void {
let t0 = clock_monotonic_ns()
// ... work ...
let elapsed = clock_elapsed_ns(t0)
let cycles = clock_rdtsc() // RDTSC on x86_64, cntvct on ARM64
}| Function | Source |
|---|---|
clock_rdtsc() | RDTSC / ARM virtual counter |
clock_monotonic_ns() | clock_gettime / QPC |
clock_elapsed_ns(start) | Helper using monotonic clock |
USB & serial ports
Modules: usb.ny, serial.ny
USB enumeration
Lists devices via Linux sysfs (/sys/bus/usb/devices). macOS/Windows: use FFI or language bridge for full IOKit/SetupAPI access.
import "stdlib/os.ny"
fn main() {
let n = usb_device_count()
print(n)
if n > 0 {
print(usb_device_vid(0)) // vendor ID hex
print(usb_device_pid(0)) // product ID hex
print(usb_device_path(0)) // sysfs path on Linux
}
}import "stdlib/os.ny"
fn main() -> void {
let n = usb_device_count()
print(n)
if n > 0 {
print(usb_device_vid(0)) // vendor ID hex
print(usb_device_pid(0)) // product ID hex
print(usb_device_path(0)) // sysfs path on Linux
}
}Serial (UART / COM)
import "stdlib/os.ny"
fn main() {
let port = "/dev/ttyUSB0" // Windows: "COM3"
let h = serial_open(port, 115200)
if h >= 0 {
serial_write(h, "AT\r\n")
print(serial_read(h, 256))
serial_close(h)
}
}import "stdlib/os.ny"
fn main() -> void {
let port = "/dev/ttyUSB0" // Windows: "COM3"
let h = serial_open(port, 115200)
if h >= 0 {
serial_write(h, "AT\r\n")
print(serial_read(h, 256))
serial_close(h)
}
}Typical paths: Linux /dev/ttyUSB0, macOS /dev/cu.usbserial-*, Windows COM3.
Signals & kernel IPC
Modules: signals.ny, mqueue.ny
OS signal handling
Nyra uses a poll model: a C handler sets a flag; your main loop calls signal_poll. Do not run Nyra code inside the Unix signal handler.
import "stdlib/os.ny"
fn main() {
signal_install(SIGINT)
let mut ticks = 0
while ticks < 1 {
if signal_poll(SIGINT) {
print("Ctrl+C — shutting down")
break
}
ticks = ticks + 1
}
}import "stdlib/os.ny"
fn main() -> void {
signal_install(SIGINT)
let mut ticks: i32 = 0
while ticks < 1 {
if signal_poll(SIGINT) {
print("Ctrl+C — shutting down")
break
}
ticks = ticks + 1
}
}Constants: SIGINT (2), SIGTERM (15), SIGSEGV (11). Windows: supported for SIGINT/SIGTERM via console control events.
Message queues (Linux/macOS/Windows)
import "stdlib/os.ny"
fn main() {
let mq = mqueue_open("nyra_demo", 8, 256)
mqueue_send(mq, "payload")
print(mqueue_recv(mq, 256))
mqueue_close(mq)
}import "stdlib/os.ny"
fn main() -> void {
let mq = mqueue_open("nyra_demo", 8, 256)
mqueue_send(mq, "payload")
print(mqueue_recv(mq, 256))
mqueue_close(mq)
}Linux: POSIX mqueue (librt). macOS: in-process best-effort queue (name ignored). Windows: mailslot best-effort queue (local IPC).
Hardware crypto & permissions
Modules: hw_crypto.ny, permissions.ny — OS-level hardware RNG and privilege drop. For everyday app randomness (dice, shuffles, simulations), use random(), random(min, max), random_f64() — ChaCha20 seeded from OS/hardware entropy. Import stdlib/random.ny for shuffle_pick. For application crypto (SHA, HMAC, AES), use stdlib/crypto/mod.ny.
Everyday random vs hardware TRNG
| API | Module | Use when |
|---|---|---|
random(min, max) | builtin / random.ny | Games, shuffles — ChaCha20 stream |
random_bytes(n) | stdlib/crypto/random.ny | Hex-encoded random bytes (same ChaCha20 backend) |
hw_random_bytes(n) | stdlib/os/hw_crypto.ny | Raw OS TRNG for keys, tokens, crypto seeds |
Hardware TRNG
import "stdlib/os.ny"
fn main() {
let raw = hw_random_bytes(32)
print(raw.len())
print(hw_secure_enclave_available())
}import "stdlib/os.ny"
fn main() -> void {
let raw = hw_random_bytes(32)
print(raw.len())
print(hw_secure_enclave_available())
}Full Secure Enclave / Intel SGX key sealing needs platform-specific FFI beyond this hook.
Privilege dropping & sandbox hints
import "stdlib/os.ny"
fn main() {
print(perm_getuid())
print(perm_geteuid())
// perm_drop_to_uid(1000) // needs root; drops to user 1000
// perm_chroot("/var/jail") // Linux/macOS: chroot (needs privileges). Windows: best-effort SetCurrentDirectory.
print(perm_sandbox_seatbelt_available()) // macOS Seatbelt present
}import "stdlib/os.ny"
fn main() -> void {
print(perm_getuid())
print(perm_geteuid())
// perm_drop_to_uid(1000) // needs root; drops to user 1000
// perm_chroot("/var/jail") // Linux/macOS: chroot (needs privileges). Windows: best-effort SetCurrentDirectory.
print(perm_sandbox_seatbelt_available()) // macOS Seatbelt present
}Combined advanced demo: nyra run examples/os/adv/main.ny
GPIO / specialized hardware (FFI)
Nyra does not ship a GPIO or serial-port stdlib like Arduino. Use one of these patterns:
1. FFI with a C library
When a mature C API exists (libusb, libserialport, OpenSSL, SQLite, …), declare it with extern fn and link the native library:
extern fn strlen(s){
print(strlen("hello"))
}extern fn strlen(s){
print(strlen("hello"))
}In nyra.mod:
link usb-1.0
link -L /opt/homebrew/lib
Guides: FFI & ABI · NyraPkg (link, link-source) · examples/ffi/call_libc/
2. Language bridge (Python / Node / Java)
When the hardware library lives in pip, npm, or Maven, run a subprocess worker and exchange JSON lines:
import "stdlib/bridge/mod.ny"
fn main() {
let req = bridge_op_add(1, 2)
let out = bridge_exec_arg("python3", "workers/bridge_worker.py", req)
print(bridge_result(out))
}import "stdlib/bridge/mod.ny"
fn main() -> void {
let req = bridge_op_add(1, 2)
let out = bridge_exec_arg("python3", "workers/bridge_worker.py", req)
print(bridge_result(out))
}Extend workers to call pyserial, RPi.GPIO, numpy, etc. Nyra stays the orchestrator. See examples/bridge/ and FFI & ABI.
3. Network-connected devices
Hardware that speaks TCP or HTTP can be reached with the net stdlib — no native driver required:
import "stdlib/net/tcp.ny"
import "stdlib/net/http/mod.ny"import "stdlib/net/tcp.ny"
import "stdlib/net/http/mod.ny"See net/http API.
4. Bare-metal / drivers (no_std)
For kernels, firmware, or memory-mapped I/O:
no_stdat top of file (or--no-stdCLI flag)import "stdlib/core/mem.ny"formalloc,memcpy, volatile MMIO*Ttyped raw pointers insideunsafeblocks
See Compilation targets (--freestanding).
Cheat sheet
| Goal | Approach |
|---|---|
| Create / read files | import "stdlib/fs.ny" |
| Detect OS, read env, battery | import "stdlib/os.ny" |
| Run shell / system command | Command_new / .run — stdlib/process |
| Capture subprocess stdout | stdlib/bridge/mod.ny |
| Prompt user on terminal | input("...") builtin |
| Existing C library | extern fn + link in nyra.mod |
| Python / npm hardware libs | stdlib/bridge/mod.ny |
| CPU affinity / RDTSC | affinity.ny, clocks.ny |
| USB list / serial port | usb.ny, serial.ny |
| SIGINT / mqueue | signals.ny + mqueue.ny (Linux POSIX; macOS/Windows best-effort) |
| Random / HW crypto | random.ny, hw_crypto.ny, permissions.ny |
| CPU topology / disk / netif | cpu.ny, storage.ny, netif.ny |
| Embedded / MMIO | no_std + stdlib/core/mem.ny |
| Device over network | stdlib/net/tcp.ny or net/http |
Full example
Combine filesystem, OS detection, user input, and battery into one project (myapp/main.ny):
import "stdlib/fs.ny"
import "stdlib/os.ny"
fn main() {
let name = "Ada"
let info = `OS: ${platform_name()}
PID: ${os_getpid()}
HOME: ${os_getenv("HOME")}
User: ${name}
`
write_file("system_info.txt", info)
print(read_file("system_info.txt"))
let bat = battery_percent()
if bat >= 0 {
print(`Battery: ${bat}%`)
}
}import "stdlib/fs.ny"
import "stdlib/os.ny"
fn main() -> void {
let name: string = "Ada"
let info = `OS: ${platform_name()}
PID: ${os_getpid()}
HOME: ${os_getenv("HOME")}
User: ${name}
`
write_file("system_info.txt", info)
print(read_file("system_info.txt"))
let bat = battery_percent()
if bat >= 0 {
print(`Battery: ${bat}%`)
}
}cd myapp
nyra run .