Structure is the lifetime.
A locus owns its region and children. Dissolve it and the owned memory is reclaimed deterministically.
a language for running systems
Write the services, messages, ownership, placement, and recovery as one program. Hale turns that description into direct calls, queues, sockets, shared memory, or broker adapters — and checks the resulting system before it runs.
$ curl -fsSL https://hale-lang.org/install.sh | sh
room.hl
type Msg { room: String; user: String; text: String; }
topic Posted { payload: Msg; keyed_by room; }
topic Broadcast { payload: Msg; }
locus Room {
params { name: String = "lobby"; }
bus {
subscribe Posted as on_post where key == self.name;
publish Broadcast;
}
fn on_post(m: Msg) {
Broadcast <- m;
}
} a roomlocus Room each posted messagesubscribe Posted in that roomkeyed_by room relay it to everyonepublish Broadcast
why now
More implementations will be produced by more people and more tools. The scarce part is knowing what each part may touch, publish, block on, allocate, depend on, or destroy. Hale makes those facts part of the program instead of leaving them in review comments and deployment lore.
one typed edge, several mechanisms
The loci and their topic sends stay put. Change main to
decide where they run and how each edge travels.
main.local.hl
main locus App {
params {
ingest: Ingest = Ingest { };
rollup: Rollup = Rollup { };
}
} main.pinned.hl
main locus App {
params {
ingest: Ingest = Ingest { };
rollup: Rollup = Rollup { };
}
placement {
ingest: pinned(core = 1);
rollup: cooperative(pool = compute);
}
} main.unix.hl
main locus App {
params {
ingest: Ingest = Ingest { };
rollup: Rollup = Rollup { };
}
bindings {
Samples: unix("/run/samples.sock");
}
} main.shm.hl
main locus App {
params {
ingest: Ingest = Ingest { };
rollup: Rollup = Rollup { };
}
bindings {
Samples: shm_ring("/samples",
slot_count: 1024,
on_overflow: drop)
where intra_machine, zero_copy;
}
} main.adapter.hl
main locus App {
params {
ingest: Ingest = Ingest { };
rollup: Rollup = Rollup { };
}
bindings {
Samples: NatsAdapter {
url: "nats://prod:4222"
};
}
} A successful send means the broker accepted the message under the selected binding's guarantee. A binding that cannot be opened fails the declaring locus at birth; it does not leave a route that claims success while dropping everything.
one recursive structure
A locus is not just an actor or an object. It is where domain identity, owned state, memory lifetime, execution, children, and failure policy meet.
main locus Telemetry owns the system root deployment authority
typed broker plane subjects are above publishers and subscribers
SamplesSnapshot
locus Ingest owns socket + receive arena pinned · core 1
locus Rollup owns windows + counters pool · compute
locus Metrics owns HTTP server state pool · io
A locus owns its region and children. Dissolve it and the owned memory is reclaimed deterministically.
Publishers and subscribers declare intent. They do not hold references to one another.
The same locus can share a pool, own a thread, pin to a core, or sit across a process boundary.
Structural failures reach the parent with enough context to restart, quarantine, or let them bubble.
causality as a contract
A call graph stops at a publish. Hale continues through the typed bus graph, across republishers and into the loci that receive the result.
report.hl
@effects(depends: {PublicSummary, ConfigChanged})
locus Report {
bus {
subscribe PublicSummary as on_summary;
subscribe ConfigChanged as configure;
}
}
The declaration says exactly which subjects may transitively
influence Report. It is a complete set, not a comment.
hale check
declared dependency set violated:
Report can transitively depend on PrivateFeed
Path:
subject PrivateFeed
-> Relay
-> subject PublicSummary
-> Report A violation names the architectural path, including the innocent- looking republished subject that hid the original dependency.
Block, syscall, time, entropy, FFI, publish, spawn, recursion, and allocation.
What a function may cause through calls and topic publication.
What may transitively influence a locus through its subscriptions.
Hard bounds such as zero allocations per call on a hot path.
one line of development
Hale's direction is not a pile of unrelated features. It is a progression from architecture as source to architecture that can be observed, replayed, and safely changed.
shipped Describe loci, topics, ownership
shipped Check effects, causality, budgets
shipped Deploy placement and bindings
shipped Observe runtime topology and events
RFC Replay recorded deterministic execution
RFC Adapt verified deployment transitions
Deterministic replay and verified deployment transitions are RFC work. They are shown here as direction, not as shipped commands.
the toolchain
Build, run, check, test, format, document, benchmark, and verify. The LSP and MCP server expose the same structured diagnostics to editors, CI, and other tools.
supervision.hl
locus WorkerPool {
accept(c: Worker) { }
on_failure(c: Worker, err: ClosureViolation) {
restart(c);
}
} built to be inspected
the shortest useful introduction
The playground tour starts with ordinary code and ends with typed communication, ownership, supervision, and placement.