The five space types
Each space type is a contract with the compiler — what it does, what it cannot do.
A space (@) always has a type. The type is a semantic contract between the author and the compiler: it says what the space is for, what runtime guarantees it offers, and which validations the compiler should apply.
There are five core types. Each one is small. Each one is opinionated. Together they cover most of what you’d build.
:UI — interactive surfaces
A :UI space is a declarative surface for dashboards, input forms, and control panels. Instead of writing concrete UI code, you describe intent — layout, validation, flow — and the compiler synthesises the runtime UI from your declaration.
@userSettings:UI
>>> A panel for managing personal account settings.
>>> Bind fields to %profileData.
>>> When %onSubmit, delegate to $validateProfile.
UI spaces are event-driven (%onEnter, %onSubmit, %onError), state lives in named data resources, and rendering is stateless between events.
:IO — sensors, devices, streams
An :IO space anchors your program to physical devices and network streams: cameras, microphones, sockets, actuators. You say what to capture, when it flows, and how to handle faults — without embedding driver code.
@camFeed:IO
>>> Camera interface publishing raw JPEG frames.
>>> On %onEnter, trigger $io_reader(out=%jpgFrameStream).
>>> Each %sensorUpdate delegates to $decodeFrame(in=%jpgFrameStream).
:IO spaces are kept small on purpose: they ingest or emit, then hand off to :FUNC or :AGENTIC for transformation.
:DATA — persistence
A :DATA space is a device-agnostic, network-aware dataspace that behaves like a logical folder hierarchy or object store. You declare what to store, version, or replicate — without prescribing the backend.
@appLogs:DATA
>>> Logical folder for daily application logs.
>>> Retains the last 30 days, mirrors to cloud storage.
>>> On %onEnter, trigger $data_reader(path="/today", out=%logBatch).
All persistence flows through :DATA spaces. :FUNC and :AGENTIC spaces are stateless across invocations; if they need durable state, they read or write through a :DATA space with an explicit path.
:FUNC — deterministic logic
A :FUNC space handles incoming work using deterministic functions only — no LLMs. Routing happens through a $func_main selector whose conditions are written in plain English in vibe blocks but compiled to deterministic checks.
@textUtils:FUNC
>>> Deterministic text utilities; selection is based on a clear task hint.
>
$func_main
>>> If the task hint is "sanitize", use $sanitizeInput to clean the text.
>>> If the task hint is "tokenize", use $tokenize to split the text.
>>> Otherwise, use $fallback with the original work.
Use :FUNC when the answer must be the same every time, when you’re calling audited code, or when you simply don’t need the AI in the loop.
:AGENTIC — LLM-routed
An :AGENTIC space handles incoming work by engaging an LLM-backed selector to determine what to do next. This is for open-ended requests: free-form natural language, ambiguous phrasing, or domains where intent must be inferred.
@supportDesk:AGENTIC
>>> Routes incoming customer queries to the right task by semantic interpretation.
>
$agentic_main
>>> Interpret the request using the customer's message and metadata.
>>> If the query is about order status, call $lookupOrder.
>>> If it is a complaint, call $logComplaint.
>>> Otherwise, call $fallback.
:AGENTIC spaces are stateless across invocations like :FUNC spaces — durable state still flows through :DATA. The difference is who decides what to call.
Choosing a type
In practice the choice is mechanical:
- Talking to a human?
:UI. - Talking to a device?
:IO. - Storing or reading bytes?
:DATA. - Deterministic routing?
:FUNC. - Semantic routing?
:AGENTIC.
Most non-trivial programs use three or four of them. The compiler uses the type to apply static validation (e.g., disallowing persistent writes inside :FUNC) and to guide where the runtime should place the space — edge, cloud, or client.
Next: putting two spaces together.