geas

statically typed · contract oriented · v0.7.1

The contract is the unit of code.

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 held

The 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

Everything is a contract

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

Any language can sign

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

Errors are values

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.

Signed, fulfilled, broken

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.

  1. Unsigned

    A declaration. Abstract pledges wait for the host to bind them.

  2. Signed

    Shape checked, vows locked, schema reconciled. The instance is live.

  3. Fulfilled

    The policy holds. Every required pledge has latched Ok.

  4. Partially fulfilled

    Some subcontracts hold. The host reads exactly what is pending.

  5. Broken

    Torn down by policy or by break. Every allocation is reclaimed.

Vows

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.

Requirements as logic

Fulfill, partial, and break are written as boolean policy over pledges and subcontracts. The compiler proves fulfill and break can never hold at once.

Partial fulfillment

Subcontracts name groups of pledges. At any moment the host reads which groups are fulfilled, pending, or broken, with latched errors attached.

The iname table

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.

The store layer

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.

The bridge

One command emits a gRPC surface with Go and TypeScript session wrappers. Park a live instance in one process and resume it in another.

Build it and make it keep a promise

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.