Skip to content

Overview and philosophy

Geas is a statically typed, contract oriented language that sits between other languages. A program declares contracts, other languages sign and fulfill them across the C ABI, and a shared runtime enforces the terms. No protocol definitions, no serialization boilerplate, no hand written FFI bindings. Any language that can call into a shared C library can talk through a Geas contract. It also works as a standalone language for writing contract libraries and contract driven programs.

A geas, in Celtic myth, is a binding obligation laid on a person: keep it and it protects you, break it and the consequences find you. That is the whole design of the language, so the name is not a metaphor, it is a description.

The compiler is geas, source files end in .geas, a compiled module ends in .geas.so, and the runtime is a shared C library, libgeasrt.

Five commitments define the language. Where the specification states a rule, the rule is the one geas enforces and the runtime holds.

The contract is the unit of the language, not the function. A program is a set of contracts other languages sign and fulfill. A contract groups the state a signing locks, the commitments a host fulfills, the internal methods those commitments share, and the policy that decides when the whole is fulfilled, partially fulfilled, or broken. See Contracts and the lifecycle for a walk through the model.

Ownership is always clear. Visibility is always stated: a declaration is exported from its package by default, and internal confines it to its own directory. Numeric widths are fixed and never convert on their own; a widening or narrowing is a type error unless written explicitly. Every fallible pledge returns a Result or an Option, never a bare value that might not be there.

There are no exceptions, and nothing throws across a language boundary. A failure is a value the caller matches on. A pledge’s error type is an ordinary sum, and the runtime keeps a latched error payload readable until the contract is explicitly broken.

The runtime owns every heap allocation that crosses a boundary, data passes by value in both directions, and a reference is copied on entry and written back on return, never held by Geas. A host never frees what Geas returns, and Geas never holds a pointer into host memory after a call returns. The repository proves the claim with two hosts, C and Python, driving the same contracts with no generated bindings. See Interop.

A contract’s shape, its vows, its fulfillment policy, and its version are checked at the boundary, so a mismatch is a descriptive error and never silent corruption. Every contract and pledge is registered under a mangled name that bakes in a hash of the type signature and the contract version, so a host that signs against the wrong version is refused at discovery rather than corrupted at call time.

contract PaymentService {
incorporate Loggable
vow currency: String = "USD"
subcontract Validation {
pledge validate_card(card: Card) -> Result<Bool, ValidationError>
pledge validate_amount(amount: Float) -> Result<Bool, ValidationError>
}
subcontract Processing {
pledge charge(card: Card, amount: Float) -> Result<Receipt, PaymentError>
}
pledge notify_user(receipt: Receipt) -> Result<Unit, NotifyError>
requirements {
fulfill: Validation && Processing && notify_user
partial: Validation || Processing
break: !Validation && !Processing && !notify_user
}
}

A vow is an immutable field locked when the contract is signed. A pledge is a callable commitment with a declared return type. A clause is an internal method that never leaves its contract. A subcontract groups pledges so a contract can be partially fulfilled, and the requirements block writes the fulfillment policy as boolean logic over those groups. The policy itself is described in Requirements and state.

Geas is built in layers, each one widening where a contract can be fulfilled without changing what a contract is.

Layer 1: the core language and the in process runtime. Complete. The full pipeline runs, lexer through type checker through codegen, and the C runtime enforces the lifecycle: per pledge latching, a worker pool, instance owned allocation, and reclamation at break. Hosts in C and Python drive contracts through the ABI, and geas build --bin links a standalone executable.

Layer 2: the network runtime. Runs. The same contracts are served over a socket and fulfilled across a machine boundary with the host code unchanged. The wire is gRPC: geas emit-proto writes the wire surface, and a client in any gRPC language builds from the emitted artifacts with stock tooling. A park store lets a session survive its stream, its client, and its server.

Layer 3: the store runtime. In progress. A contract backed by a database: a schema is a vow that locks at sign, a transactional subcontract is a transaction, and pledges read and write rows through the store standard library. The reference backend is SQLite, vendored into libgeasrt. The core surface works today; parts of the query surface are still landing.

Layer 4: the mesh. Planned. See Status and roadmap for what exists today and what does not.