statically typed · contract oriented · v0.7.1
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 the runtime enforces the terms.
payment.geas
contract PaymentService {
vow currency: String = "USD"
subcontract Validation {
pledge validate_amount(amount: Float) -> Result<Bool, Int> {
if amount > 0.0 {
return Ok(true)
}
return Err(1)
}
}
subcontract Processing {
// Abstract: the host binds this before sign.
pledge charge(card: String, amount: Float) -> Result<Bool, Int>
}
requirements {
fulfill: Validation && Processing
partial: Validation || Processing
break: !Validation && !Processing
}
}host.py · Python signs the contract
from geas import Runtime, Ok, Err
def charge(inst, args):
card, amount = args
return Ok(True) if amount > 0 else Err(41)
with Runtime("libgeasrt.so") as rt:
rt.load("libpayment.geas.so")
rt.bind("PaymentService.charge", charge)
rt.freeze()
c = rt.sign("PaymentService", vows={"currency": "EUR"})
c.fulfill_sync("validate_amount", 25.0)
c.fulfill_sync("charge", "4111 1111", 25.0)
p = c.partial()
# p.fulfilled == ["Validation", "Processing"]
# the instance is Fulfilled, the terms heldThe contract compiles to a shared library. Python signs it over the C ABI with a plain ctypes wrapper, binds the abstract pledge with an ordinary function, and overrides a vow at sign time. No header was parsed and no code was generated. A C, Go, or Node host drives the same module the same way.
the unit
The contract is the unit of code, not the function. A contract groups vows, pledges, and clauses under a requirements policy, and the runtime walks it through a real lifecycle. State never regresses and nothing happens off the record.
the boundary
A compiled contract is a shared library with its terms baked in. Any language that can call into a C library can sign it, fulfill its pledges, and break it. No protocol definitions, no serialization boilerplate, no generated bindings.
the terms
Nothing throws, and nothing throws across a language boundary. Every fallible pledge returns Result or Option, match is exhaustive, and a failed pledge latches broken with its error payload readable by name.
A geas, in Celtic myth, is a binding obligation. Keep it and it protects you, break it and the consequences find you. The name is not a metaphor, it is a description.
Every instance moves through one enforced lifecycle. After each pledge outcome the runtime re evaluates the policy, checking break first, then fulfill, then partial. A latched state never regresses.
A declaration. Abstract pledges wait for the host to bind them.
Shape checked, vows locked, schema reconciled. The instance is live.
The policy holds. Every required pledge has latched Ok.
Some subcontracts hold. The host reads exactly what is pending.
Torn down by policy or by break. Every allocation is reclaimed.
Immutable fields locked at sign time. The host can set them when it signs, defaults fill the rest, and an unbound vow refuses the sign.
Fulfill, partial, and break are written as boolean policy over pledges and subcontracts. The compiler proves fulfill and break can never hold at once.
Subcontracts name groups of pledges. At any moment the host reads which groups are fulfilled, pending, or broken, with latched errors attached.
Every contract and pledge lives in a queryable registry keyed by name, shape hash, and version. A mismatch is a clean error at discovery, never silent corruption.
A schema block declares a table the way a vow declares a value. Transactions are subcontracts, SQLite ships in the runtime, and storage never shows through the ABI.
One command emits a gRPC surface with Go and TypeScript session wrappers. Park a live instance in one process and resume it in another.
git clone https://github.com/choice404/geas
cd geas
make smoke
target/dusk-out/geas build skeleton/hello.geas
target/dusk-out/geas build --bin skeleton/main_demo.geas
You need the dusk toolchain 1.5.3 or newer,
a C compiler, and make. The compiler is written in dusk, the runtime is plain C,
and make smoke builds both and runs the end to end check.