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.
The design stance
Section titled “The design stance”Five commitments define the language. Where the specification states a rule, the rule is the one geas enforces and the runtime holds.
Everything is a contract
Section titled “Everything is a contract”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.
Nothing is implicit
Section titled “Nothing is implicit”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.
Errors are values
Section titled “Errors are values”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.
Interop is the primary goal
Section titled “Interop is the primary goal”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.
The runtime enforces the terms
Section titled “The runtime enforces the terms”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.
A taste of the language
Section titled “A taste of the language”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.
The layer model
Section titled “The layer model”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.
Where to go next
Section titled “Where to go next”- Getting started builds the toolchain and runs a contract end to end.
- Type system covers the value model, records, sums,
Option, andResult. - Source files and packages covers imports, packages, and visibility.
- Grammar is the condensed normative surface.