Skip to content

Program core

The Core v2 definitions and theorems for programs that issue typed commands, with the companion modules built on them and their status.

The program core describes programs that return a value or issue a typed command and continue from its reply, and states protocol safety as a separate proposition. It is Lean source under lean/core/, published into a store and checked by the pinned Lean 4.34.1 kernel like any other library. Signatures on this page are copied from the files; proofs are omitted.

FileWhat it isStatus
lean/core/CoreV2.leanCore v2: Contract has pre, next and postaccepted through the API, reverify agreeing (crates/lema-cli/tests/core_v2.rs)
lean/core/Core.leanCore v1: Contract has pre and next onlythe authoring view of the accepted Core v1 objects
lean/core/CoreBridge.leanv1’s Safe, Implements and Simulates are v2’s with unpromisedaccepted (core_v2.rs)

Core v2 elaborates Prog and Impl with the seeds of the accepted Core v1 objects (lean/core/seeds.json), so they are the same objects by identity, and Core v1 programs and implementations are Core v2 ones. All names are in namespace LemaDraft. Files that use the core receive it by identity, for example Received.Contract bound to Contract’s type, Received.mkContract to its constructor (role ctor), and Received.progRec to Prog’s recursor (role generated:rec).

Types

lean/core/CoreV2.lean
inductive Prog (C : Type) (R : C → Type) (A : Type) : Type where
  | ret : A → Prog C R A
  | call : (c : C) → (R c → Prog C R A) → Prog C R A

structure Contract (C : Type) (R : C → Type) (S : Type) where
  pre : S → C → Prop
  next : (s : S) → (c : C) → R c → S
  post : S → (c : C) → R c → Prop

structure Impl (C : Type) (R : C → Type) (M : Type) where
  step : M → (c : C) → R c × M
NameMeaning
Prog C R Aa program over commands C with reply types R c, returning A: ret a stops with a; call c k issues c and continues with k r for its reply r
Contract C R Sa protocol over abstract state S: when a command may run (pre), how a reply changes the state (next), and what the reply promises (post)
Impl C R Ma total mathematical operation model with private state M, not a claim about blocking OS I/O

Judgments

lean/core/CoreV2.lean
def Safe {C : Type} {R : C → Type} {S A : Type}
    (K : Contract C R S) (Q : S → A → Prop) : Prog C R A → S → Prop :=
  Prog.rec (fun a s => Q s a)
    (fun c _ ih s => K.pre s c ∧ ∀ r, K.post s c r → ih r (K.next s c r))

def Implements {C : Type} {R : C → Type} {S M : Type}
    (K : Contract C R S) (h : Impl C R M) (Inv : S → M → Prop) : Prop :=
  ∀ s m c, Inv s m → K.pre s c →
    K.post s c (h.step m c).1 ∧ Inv (K.next s c (h.step m c).1) (h.step m c).2

def Simulates {C : Type} {R : C → Type} {S M N : Type}
    (K : Contract C R S) (first : Impl C R M) (second : Impl C R N)
    (Rel : S → M → N → Prop) : Prop :=
  ∀ s m n c, Rel s m n → K.pre s c →
    ∃ r m' n', first.step m c = (r, m') ∧ second.step n c = (r, n') ∧
      K.post s c r ∧ Rel (K.next s c r) m' n'
NameMeaning
Safe K Q p sfrom state s, p issues only commands K permits, for every reply that keeps K’s promise, and ends where Q holds of the final state and result
Implements K h Invh keeps K’s promises and preserves the invariant Inv between abstract and private state
Simulates K first second Relfrom Rel-related states, two implementations give the same promised reply and stay related

Safe is relative to the promise: a promise no reply keeps makes it trivial. It says something about runs only through sound and replacement, whose premises make implementations keep the promise.

Composition and semantics

lean/core/CoreV2.lean
def bind {C : Type} {R : C → Type} {A B : Type}
    (p : Prog C R A) (f : A → Prog C R B) : Prog C R B :=
  match p with
  | .ret a => f a
  | .call c k => Prog.call c (fun r => bind (k r) f)

def interp {C D : Type} {R : C → Type} {E : D → Type} {A : Type}
    (module : (d : D) → Prog C R (E d)) (p : Prog D E A) : Prog C R A :=
  match p with
  | .ret a => Prog.ret a
  | .call d k => bind (module d) (fun r => interp module (k r))

def run {C : Type} {R : C → Type} {M A : Type}
    (h : Impl C R M) (p : Prog C R A) : M → A × M :=
  match p with
  | .ret a => fun m => (a, m)
  | .call c k => fun m => let answer := h.step m c
                         run h (k answer.1) answer.2

def trace {C : Type} {R : C → Type} {M A : Type}
    (h : Impl C R M) (p : Prog C R A) : M → List (Sigma R) :=
  match p with
  | .ret _ => fun _ => []
  | .call c k => fun m => let answer := h.step m c
                         Sigma.mk c answer.1 :: trace h (k answer.1) answer.2

def after {C : Type} {R : C → Type} {S : Type}
    (K : Contract C R S) (s : S) : List (Sigma R) → S
  | [] => s
  | event :: rest => after K (K.next s event.1 event.2) rest

def Allowed {C : Type} {R : C → Type} {S : Type}
    (K : Contract C R S) (s : S) : List (Sigma R) → Prop
  | [] => True
  | event :: rest => K.pre s event.1 ∧ K.post s event.1 event.2 ∧
      Allowed K (K.next s event.1 event.2) rest

def unpromised {C : Type} {R : C → Type} {S : Type}
    (pre : S → C → Prop) (next : (s : S) → (c : C) → R c → S) : Contract C R S :=
  ⟨pre, next, fun _ _ _ => True⟩
NameMeaning
bind p fsequencing: f substituted for each returned value
interp module plinking: each command of p replaced by a program over another signature
run h p mthe result and final private state of running p on h from m
trace h p mthe command and reply pairs of that run
after K s tthe abstract state after trace t from s
Allowed K s teach command of t permitted and each reply promised
unpromised pre nexta contract that promises nothing about replies: Core v1’s contracts

run and trace are mathematical semantics, not evidence about actual I/O.

Theorems

lean/core/CoreV2.lean
theorem sound {C : Type} {R : C → Type} {S M A : Type}
    (K : Contract C R S) (h : Impl C R M) (Inv : S → M → Prop)
    (law : Implements K h Inv) (Q : S → A → Prop) (p : Prog C R A) :
    ∀ s m, Safe K Q p s → Inv s m →
      Allowed K s (trace h p m) ∧
      Inv (after K s (trace h p m)) (run h p m).2 ∧
      Q (after K s (trace h p m)) (run h p m).1

theorem replacement {C : Type} {R : C → Type} {S M N A : Type}
    (K : Contract C R S) (first : Impl C R M) (second : Impl C R N)
    (Rel : S → M → N → Prop) (law : Simulates K first second Rel)
    (Q : S → A → Prop) (p : Prog C R A) :
    ∀ s m n, Safe K Q p s → Rel s m n →
      (run first p m).1 = (run second p n).1 ∧
      trace first p m = trace second p n ∧
      Rel (after K s (trace first p m)) (run first p m).2 (run second p n).2

theorem safe_bind {C : Type} {R : C → Type} {S A B : Type}
    (K : Contract C R S) (Q : S → B → Prop)
    (p : Prog C R A) (f : A → Prog C R B) :
    ∀ s, Safe K Q (bind p f) s ↔
      Safe K (fun t a => Safe K Q (f a) t) p s

theorem safe_interp {C D : Type} {R : C → Type} {E : D → Type}
    {S T A : Type} (K : Contract C R S) (G : Contract D E T)
    (Rel : T → S → Prop) (module : (d : D) → Prog C R (E d))
    (law : ∀ t s d, Rel t s → G.pre t d →
      Safe K (fun s' r => G.post t d r ∧ Rel (G.next t d r) s') (module d) s)
    (Q : T → A → Prop) (p : Prog D E A) :
    ∀ t s, Safe G Q p t → Rel t s →
      Safe K (fun s' a => ∃ t', Rel t' s' ∧ Q t' a) (interp module p) s

theorem safe_mono {C : Type} {R : C → Type} {S A : Type}
    (K : Contract C R S) (P Q : S → A → Prop)
    (implies : ∀ s a, P s a → Q s a) (p : Prog C R A) :
    ∀ s, Safe K P p s → Safe K Q p s

theorem run_bind {C : Type} {R : C → Type} {M A B : Type}
    (h : Impl C R M) (p : Prog C R A) (f : A → Prog C R B) :
    ∀ m, run h (bind p f) m = run h (f (run h p m).1) (run h p m).2

theorem trace_bind {C : Type} {R : C → Type} {M A B : Type}
    (h : Impl C R M) (p : Prog C R A) (f : A → Prog C R B) :
    ∀ m, trace h (bind p f) m =
      trace h p m ++ trace h (f (run h p m).1) (run h p m).2

theorem safe_unpromised {C : Type} {R : C → Type} {S A : Type}
    (pre : S → C → Prop) (next : (s : S) → (c : C) → R c → S)
    (Q : S → A → Prop) (c : C) (k : R c → Prog C R A) (s : S) :
    Safe (unpromised pre next) Q (Prog.call c k) s ↔
      pre s c ∧ ∀ r, Safe (unpromised pre next) Q (k r) (next s c r)
TheoremStates
sounda safe program run on an implementation that keeps the contract’s promises produces an allowed trace, keeps the invariant, and meets its postcondition
replacementtwo implementations related by Simulates give every safe program the same result and trace, and related final states
safe_bindsequencing is safe exactly when the first program establishes the continuation’s safety
safe_interplinking: when each substituted command keeps its high-level transition and promise, a safe high-level client stays safe, through the relation
safe_monoa weaker postcondition keeps a program safe
run_bind, trace_bindrun and trace of a sequence are those of its parts
safe_unpromisedwithout promises, Safe asks about every reply, as in Core v1

safe_ret and safe_call unfold Safe one step (both by Iff.rfl). The file’s example, fresh handles, adds twoHandles_safe, counter_implements, twoHandles_runs (sound applied) and twoHandles_unsafe_unpromised (the same program is not safe without open’s promise).

Companion modules

Each file is in lean/core/. Most receive Core v2 by identity; Replies.lean, Durable.lean and Escrow.lean receive nothing, and CallGraph.lean receives Actor.lean’s mailbox. “Accepted” means accepted through the API by the test named; “checked locally” means elaborated and kernel-checked by the pinned Lean outside a store.

ModuleWhat it addsStatus
Replaces.leanReplaces s0 m0 n0 K first second Rel: Simulates from related start states; replaces_runsaccepted (replace_v2.rs); the relation the replace method reads
ReplaceV2.lean, ReplaceGeneric.leantwo counters and the claim plain_offset; the same over a parameter (generic_plain_offset)accepted (replace_v2.rs, replace_generic.rs)
Refines.leanRefines new old Rel, contract refinement with a total relation; refines_implements, refines_refl, refines_transaccepted (evolve.rs); the relation the refine method reads
Transport.leanTransported Rel Q; refines_safe, refines_runs, false_post_safe, false_post_unimplementable, replaces_conformschecked locally; store acceptance (protected.rs) written, not yet run
Protected.leanprotected-reference examples: the keyed counter wide, a refinement roomy, and checked negatives (narrow_not_refines, silent_not_refines, never_unimplementable)checked locally; store acceptance (protected.rs) written, not yet run
Handles.leanownership in contract state: a handle map with per-handle protocols; handleSafe_open and the other step lemmas, handle_counter_implements, use_after_close, double_closeaccepted (handles.rs)
Sums.leansums of two signatures: sum_implements, safe_liftL, safe_liftR, safe_seqaccepted (sums.rs)
Parallel.leanprograms over disjoint handles: schedule_independent, interleave_results, the frame rule safe_par, safe_interleaveaccepted (parallel.rs)
Actor.leanone certified actor with a bounded mailbox: busy_iff_full, busy_unchanged, every_order_preserves, actor_implementsaccepted (actor.rs)
Replies.leana request/response actor with tickets: trace_linearizable, replies_invariantaccepted with the item store (kv.rs, a suite of the retired M3/M4 apps)
Durable.leandurable actors: snapshot recovery (recover_eq), idempotency keys (retry, applied_at_most_once)accepted (durable.rs)
Escrow.leanescrow transfers between two shards through a coordinator: conservation, debits_at_most_once, credits_at_most_once, progress; compaction: step_refines, compacted_conservationaccepted (escrow.rs)
Tickets.leantickets and spawned tasks over Handles: system_safe, system_allowed, overlap, fanOut_safeaccepted (tickets.rs)
Channels.leansession channels with dual endpoints: dual_dual, client_safe, server_safe, ask_anyaccepted (channels.rs)
Fueled.leanfueled programs (FProg) over a costed vocabulary: slices_le_gap, total_work, commands_le, runF_some, runF_enough, loop_exitschecked locally; store acceptance (fueled_call_graph.rs) not confirmed
CallGraph.leanacyclic actor call graphs with bounded mailboxes: ranked_acyclic, no_wait_cycle, deadlock_free, progresschecked locally; store acceptance (fueled_call_graph.rs) not confirmed
Connection.lean, ConnectionV2.leana connection client on Core v1 and v2; a receive-before-open program is well typed but unsafeConnectionV2.lean accepted (core_v2.rs)

The records disagree on fueled_call_graph.rs: lean/core/README.md records a native pass on 17 Oct 2026, and docs/architecture.md lists it as not yet run. Test files are under crates/lema-cli/tests/.

Replacement, refinement and transport

lean/core/Replaces.lean
def Replaces {C : Type} {R : C → Type} {S M N : Type} (s0 : S) (m0 : M) (n0 : N)
    (K : Received.Contract C R S) (first : Received.Impl C R M) (second : Received.Impl C R N)
    (Rel : S → M → N → Prop) : Prop :=
  Rel s0 m0 n0 ∧ Received.Simulates K first second Rel

Simulates alone has no start states, so a relation that never holds proves it. Replaces adds related start states, and replaces_runs gives a safe client the same result from both implementations from those states.

lean/core/Refines.lean
def Refines {C : Type} {R : C → Type} {S T : Type}
    (new : Received.Contract C R T) (old : Received.Contract C R S) (Rel : S → T → Prop) : Prop :=
  (∀ s, ∃ t, Rel s t) ∧
  ∀ s t c, Rel s t → Received.pre old s c →
    Received.pre new t c ∧ ∀ r, Received.post new t c r →
      Received.post old s c r ∧ Rel (Received.next old s c r) (Received.next new t c r)

Rel must be total, so an empty relation cannot make two contracts refine. refines_implements says an implementation of new implements old from related states.

lean/core/Transport.lean
def Transported {S T A : Type} (Rel : S → T → Prop) (Q : S → A → Prop) (t : T) (a : A) : Prop :=
  ∃ s, Rel s t ∧ Q s a

refines_safe makes a client safe on old safe on new with the transported postcondition; refines_runs combines it with sound. The store pins Replaces, Refines, Transported, Implements and Safe by template ID for protected references.

Not covered

  • run and trace are checked semantics, not evidence of actual I/O.
  • The types are universe 0. safe_interp proves transported safety and the postcondition, not run or trace refinement of linking.
  • Whole-language quantitative typing (single-use ownership), linearity of OS handles, OS behaviour, compiler refinement and Lean’s consistency are not proved (lean/core/rules.md, “Remaining gaps”).
  • Protocol safety models sequencing obligations; it does not stop another external alias from reaching the same real resource.

Written against lema-lang at commit 5a94951, 17 Oct 2026.