From b90128fab372ce836bc9a37e1d0561fc65b76fd4 Mon Sep 17 00:00:00 2001 From: Sirac Ozmen Date: Wed, 4 Nov 2026 04:31:14 +0300 Subject: [PATCH 1/2] docs(maul): say how state a caller holds is checked maul-locale#334 asked how far a library checks state a caller holds: an opaque stamped formatter, a value struct with documented fields, and caller memory kept as checked. The conventions now give one rule for each: opaque state is checked by stamp and bounds only; a public value struct names what it came from instead of carrying a raw pointer and size; kept caller memory is read only within the extents recorded. --- maul/files/docs/internals/maul-conventions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/maul/files/docs/internals/maul-conventions.md b/maul/files/docs/internals/maul-conventions.md index 7144b88..25b8b71 100644 --- a/maul/files/docs/internals/maul-conventions.md +++ b/maul/files/docs/internals/maul-conventions.md @@ -95,6 +95,7 @@ The libraries are one product family: a program may link several of them, and a - Getters that cannot fail take ids and return values. Out-parameters end in `Out`. - Strings cross the API as UTF-8 with an explicit byte length, never as NUL-terminated text alone, so text from hostile sources is never measured by searching for a terminator. - A library's root object is a typed opaque pointer. Every object a root owns is named by a typed value id, a 1-based slot index (0 is null) and a generation, one struct per kind. A stale id is refused with a typed result, never dereferenced, which costs one array read and a compare. Platform handles are never identities. +- State a caller holds is checked by how a caller can get it wrong. Opaque state that only the library writes is checked by its stamp and by every index, slot and length in it being in bounds, so that no bit pattern makes a call read outside the state or the data it names; a forged one is outside the contract, and its fields' consistency with each other is not checked again. A value struct with documented fields, which a caller may copy and assign, carries no raw pointer and size into the library's data: it names the object or data it came from and an index, checked on each call, so that a field copied from another value cannot reach memory outside what it names. Caller memory a call keeps "as checked", such as a bound message, must stay unchanged as documented, and whatever it becomes, a later call reads only within the extents it recorded. - An operation that finishes later is a request: it returns its status at once and, accepted, a typed request id that exactly one completion record answers, in the owner's notification queue, with a typed outcome. Callbacks that deliver completions fire at unsafe points and leave re-entrancy rules to every caller; a queue does not. ## Floating point and determinism From eaf35b148c4ec68895c6e86245155fc7d63ec52b Mon Sep 17 00:00:00 2001 From: Sirac Ozmen Date: Wed, 4 Nov 2026 04:32:38 +0300 Subject: [PATCH 2/2] docs(maul): scope the value-struct rule to structs a call takes back --- maul/files/docs/internals/maul-conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maul/files/docs/internals/maul-conventions.md b/maul/files/docs/internals/maul-conventions.md index 25b8b71..302ed8a 100644 --- a/maul/files/docs/internals/maul-conventions.md +++ b/maul/files/docs/internals/maul-conventions.md @@ -95,7 +95,7 @@ The libraries are one product family: a program may link several of them, and a - Getters that cannot fail take ids and return values. Out-parameters end in `Out`. - Strings cross the API as UTF-8 with an explicit byte length, never as NUL-terminated text alone, so text from hostile sources is never measured by searching for a terminator. - A library's root object is a typed opaque pointer. Every object a root owns is named by a typed value id, a 1-based slot index (0 is null) and a generation, one struct per kind. A stale id is refused with a typed result, never dereferenced, which costs one array read and a compare. Platform handles are never identities. -- State a caller holds is checked by how a caller can get it wrong. Opaque state that only the library writes is checked by its stamp and by every index, slot and length in it being in bounds, so that no bit pattern makes a call read outside the state or the data it names; a forged one is outside the contract, and its fields' consistency with each other is not checked again. A value struct with documented fields, which a caller may copy and assign, carries no raw pointer and size into the library's data: it names the object or data it came from and an index, checked on each call, so that a field copied from another value cannot reach memory outside what it names. Caller memory a call keeps "as checked", such as a bound message, must stay unchanged as documented, and whatever it becomes, a later call reads only within the extents it recorded. +- State a caller holds is checked by how a caller can get it wrong. Opaque state that only the library writes is checked by its cookie, and its ABI version where it records one, and by every index, slot and length in it being in bounds, so that no bit pattern makes a call read outside the state or the data it names; a forged one is outside the contract, and its fields' consistency with each other is not checked again. A value struct with documented fields that a call takes back from the caller, which the caller may copy and assign, carries no raw pointer and size into the library's data: it names the object or data it came from and an index, checked on each call, so that a field copied from another value cannot reach memory outside what it names. A struct the library only fills in for the caller, such as a label it points at, may point into its data. Caller memory a call keeps "as checked", such as a bound message, must stay unchanged as documented, and whatever it becomes, a later call reads only within the extents it recorded. - An operation that finishes later is a request: it returns its status at once and, accepted, a typed request id that exactly one completion record answers, in the owner's notification queue, with a typed outcome. Callbacks that deliver completions fire at unsafe points and leave re-entrancy rules to every caller; a queue does not. ## Floating point and determinism