Skip to content

Profiles × Targets

Janus has two axes that determine what code can be compiled and where it can run. They are orthogonal but not freely composable. Pairing the wrong profile with the wrong target produces a compile error, not a broken binary.

// Legal
{.profile :sovereign.} // can target anything, including bare metal
{.profile :service.} // can target Linux, macOS, NexusOS, WASI
// Rejected at compile time
{.profile :service.} // + target=none → E_PROFILE_TARGET_INCOMPATIBLE

This page documents the matrix, the doctrine behind it, and the escape hatch when you need to violate it.


Profiles are documented in the Profiles overview and codified in SPEC-002. The six canonical profiles form a monotonic capability ladder:

:script ⊆ :core ⊆ :service ⊆ :cluster ⊆ :compute ⊆ :sovereign

Each rung is a strict superset of the one below. :sovereign is the top — the complete language, with no capability hidden behind a safety rail.

As of 2026-07-09, the CLI (src/profiles.zig), semantic resolver, effect graph, pipeline, and LSP all share this ladder. Legacy spellings such as :main:core, :npu:compute, and :full:sovereign are aliases, not separate enum variants. See the Profile Enum Unification release note.

Targets are documented in RFC-NNN-target-attribute and codified as the compiler’s target_triple string. The classes that matter for profile-compatibility:

Target classExamplesHosted?
linuxx86_64-linux-gnu, aarch64-linux-musl
darwinaarch64-darwin, x86_64-macos
freebsdx86_64-freebsd
nexusosriscv64-nexusos, aarch64-nexusos
wasiwasm32-wasi, wasm32-wasi-preview1✓ (sandboxed)
freestanding*-none-elf, *-uefi, *-opensbi-*, *-bare-*

The freestanding class collapses none, uefi, opensbi, and bare-metal targets into one bucket because, for profile-compatibility purposes, they are all equivalent: no OS, no hosted facilities, no allocator, no runtime.


:sovereign is the only profile that may target a freestanding environment.

Ratified 2026-07-02 (Markus ruling). Codified in SPEC-002 §2.6.5 and RFC-NNN-target-attribute §4.1.

This single rule explains why kernels, bootloaders, firmware, hypervisors, SBI, device drivers, MMIO, DMA, page tables, and NexFS all live in :sovereign — without inventing a separate :baremetal profile. The prior :bare proposal was rejected: runtime presence is modeled on the target axis, not as a profile-axis tier.

Hosted profiles (:script through :compute) define capability sets in terms of POSIX-or-better facilities:

  • processes and threads
  • file descriptors
  • sockets and network primitives
  • environment variables
  • GPU driver stacks

None of these exist on target=none, target=uefi, or target=opensbi. Permitting :service against target=none would silently produce a binary whose capability calls resolve to absent facilities. The compile-time rejection makes the asymmetry explicit.

:sovereign is the exception because its capability set makes no hosted presumption. It speaks raw memory, MMIO, and hardware primitives directly. See SPEC-225 §6 (peripherals), §12 (memory regions).


Profilelinux / darwin / freebsdnexusoswasifreestanding
:scriptE_PROFILE_TARGET_INCOMPATIBLE
:coreE_PROFILE_TARGET_INCOMPATIBLE
:serviceE_PROFILE_TARGET_INCOMPATIBLE
:clusterE_PROFILE_TARGET_INCOMPATIBLE
:computeE_PROFILE_TARGET_INCOMPATIBLE
:sovereign✓ — the only profile permitted here
  • :cluster × wasi is rejected because :cluster’s capability set (distributed actors, network message-passing) presumes network primitives that WASI does not provide.
  • :compute × wasi is permitted, but real GPU compute on WASM depends on wasm-simd and future WASI GPU proposals. The matrix marks it ✓ pending implementation feedback from the first WASM compute consumer.

The Escape Hatch: Escalation, Not Downgrade

Section titled “The Escape Hatch: Escalation, Not Downgrade”

A hosted-profile module that needs one freestanding operation does not need to become a :sovereign module. Use the SPEC-044 escalation mechanism:

// :service HTTP server that needs to call ONE MMIO routine
{.profile :service, escalations: [:sovereign].}
func read_sensor_register(addr: usize) -> u32 do
let value = escalate :sovereign do
let ptr: :sovereign *u32 = std.mem.pointer_from[u32](addr)
return ptr.*
end
return value
end

The module’s base profile stays :service — every line outside the escalate block is normal service code, audited as such. The bare-metal capability is named (:sovereign), scoped (single block), and tracked (the return value carries the :sovereign origin tag).

This is the supported path for “hosted module with one bare-metal call.” It is not a path to ship a :service kernel. Kernels live in :sovereign unconditionally — no amount of escalation turns a hosted module into a kernel.

Read SPEC-044 for the full escalation semantics, origin tags, and composition rules.


Emitted when a hosted profile is paired with a freestanding target (or when :cluster is paired with wasi). The diagnostic includes:

  1. The declared profile name
  2. The requested target name
  3. A description of what the target lacks
  4. A remediation hint pointing at either :sovereign (if you truly need freestanding) or SPEC-044 escalation (if you have a hosted module with one bare-metal call)

Emitted when the target triple cannot be classified into one of the six target classes. The diagnostic names the unrecognized triple and lists the recognized keywords (linux, darwin, freebsd, nexusos, wasi, none, uefi, opensbi, freestanding, bare).

If you hit this, either your target triple is misspelled, or you are targeting an environment Janus does not yet recognize. File an issue.


  • Validator source: compiler/semantic/profile_target_compat.zig
  • Standalone test target: zig build test-profile-target-compat
  • Pipeline integration: Wired into STAGE 1.6 (transitive stdlib + user-module preloader). The check fires after profile resolution and target triple parsing, before any code generation.