Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/rhodibot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Reads root-hygiene rules and auto-fixes what it can:
# - Delete banned files (AI.djot, duplicate CONTRIBUTING.adoc, stale snapshots)
# - Rename misnamed files (AI.a2ml → 0-AI-MANIFEST.a2ml)
# - Fix SPDX headers (AGPL → PMPL in dotfiles)
# - Fix SPDX headers (AGPL → MPL-2.0 in dotfiles)
# - Create missing required files (SECURITY.md, CONTRIBUTING.md)
# - Report unfixable issues as PR comments
#
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
for dotfile in .gitignore .gitattributes .editorconfig; do
if [ -f "$dotfile" ] && grep -q "AGPL-3.0" "$dotfile" 2>/dev/null; then
sed -i 's/AGPL-3.0-or-later/MPL-2.0/g; s/AGPL-3.0/MPL-2.0/g' "$dotfile"
FIXES="$FIXES\n- Fixed SPDX header in \`$dotfile\` (AGPL → PMPL)"
FIXES="$FIXES\n- Fixed SPDX header in \`$dotfile\` (AGPL → MPL-2.0)"
CHANGED=true
fi
done
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ Thumbs.db
/_build/
/build/
/dist/

# Idris2 proof build artifacts (any nested build/ dir + TTC output)
**/build/
*.ttc
*.ttm
/out/

# Dependencies
Expand Down
7 changes: 7 additions & 0 deletions .machine_readable/compliance/reuse/dep5
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,10 @@ License: MPL-2.0
Files: cliff.toml
Copyright: {{CURRENT_YEAR}} {{AUTHOR}} ({{OWNER}}) <{{AUTHOR_EMAIL}}>
License: MPL-2.0

# Documentation prose is CC-BY-SA-4.0 (code/config is MPL-2.0).
# Last-match-wins in the Debian copyright format, so this overrides the
# `Files: *` default above for prose docs.
Files: *.adoc *.md docs/* docs/**/*
Copyright: {{CURRENT_YEAR}} {{AUTHOR}} ({{OWNER}}) <{{AUTHOR_EMAIL}}>
License: CC-BY-SA-4.0
2 changes: 1 addition & 1 deletion .machine_readable/contractiles/trust/Trustfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ is traceable.

### license-content
- description: LICENSE contains expected identifier
- run: grep -q 'PMPL\|MPL\|MIT\|Apache\|LGPL' LICENSE
- run: grep -q 'MPL-2.0' LICENSE
- severity: warning

## Container Security
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ version = "0.1.0"
edition = "2024"
authors = ["Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>"]
description = "Meta-framework that generates new -iser projects from a target language interop description"
license-file = "LICENSE"
license = "MPL-2.0"
repository = "https://github.com/hyperpolymath/iseriser"
keywords = ["meta-framework", "code-generation", "language-interop", "scaffolding"]
categories = ["command-line-utilities", "development-tools"]
Expand Down
5 changes: 5 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ build:
test:
cargo test

# Type-check the Idris2 ABI formal proofs (requires idris2 >= 0.7.0)
proofs:
cd src/interface/abi && idris2 --build iseriser-abi.ipkg
@echo "Idris2 ABI proofs type-check cleanly"

# Run clippy lints
lint:
cargo clippy -- -D warnings
Expand Down
2 changes: 2 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
SPDX-License-Identifier: MPL-2.0

Mozilla Public License Version 2.0
==================================

Expand Down
2 changes: 1 addition & 1 deletion QUICKSTART-MAINTAINER.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Or via OPSM: `opsm update {{PACKAGE_NAME}}`

== Security Notes

* License: MPL-2.0 (Palimpsest License)
* License: MPL-2.0 (code) / CC-BY-SA-4.0 (docs)
* All dependencies SHA-pinned
* `panic-attacker` scan results: link:INSTALL-SECURITY-REPORT.adoc[]
* OpenSSF Scorecard: see badge in README
Expand Down
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ SPDX-License-Identifier: CC-BY-SA-4.0
SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
-->

[![License: MPL-2.0](https://img.shields.io/badge/license-PMPL--1.0--or--later-blue)](LICENSE) ![Palimpsest
Covenant](https://img.shields.io/badge/Palimpsest-Covenant-purple) ![29
[![License: MPL-2.0](https://img.shields.io/badge/license-MPL--2.0-blue)](LICENSE) [![Docs: CC-BY-SA-4.0](https://img.shields.io/badge/docs-CC--BY--SA--4.0-blue)](LICENSES/CC-BY-SA-4.0.txt) ![29
repos](https://img.shields.io/badge/-iser_family-29_repos-brightgreen)
![Idris2 ABI](https://img.shields.io/badge/ABI-Idris2-red) ![Zig
FFI](https://img.shields.io/badge/FFI-Zig-orange)
Expand Down Expand Up @@ -303,9 +302,7 @@ Hypatia neurosymbolic scanning.

# License

SPDX-License-Identifier: `MPL-2.0`
Code is licensed under `MPL-2.0`; documentation under `CC-BY-SA-4.0`.
Full licence texts are in [`LICENSES/`](LICENSES/).

Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath)

Licensed under the [Palimpsest
License](https://github.com/hyperpolymath/palimpsest-license).
2 changes: 1 addition & 1 deletion contractiles/trust/Trustfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Maximal trust by default — LLM may read, build, test, lint, format.

### license-content
- description: LICENSE contains expected SPDX identifier
- run: grep -q 'SPDX\|License\|MIT\|Apache\|PMPL\|MPL' LICENSE
- run: grep -q 'SPDX\|MPL-2.0' LICENSE
- severity: critical

### no-secrets-committed
Expand Down
4 changes: 2 additions & 2 deletions docs/RSR_OUTLINE.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= RSR Template Repository

image:[Palimpsest-MPL-1.0,link="https://github.com/hyperpolymath/palimpsest-license"] image:[Palimpsest,link="https://github.com/hyperpolymath/palimpsest-license"]
image:https://img.shields.io/badge/license-MPL--2.0-blue[MPL-2.0,link="LICENSES/MPL-2.0.txt"] image:https://img.shields.io/badge/docs-CC--BY--SA--4.0-blue[CC-BY-SA-4.0,link="LICENSES/CC-BY-SA-4.0.txt"]
:toc:
:sectnums:

Expand Down Expand Up @@ -78,7 +78,7 @@ just validate-rsr
|Container build (Wolfi base, Podman)

|`LICENSE`
|MPL-2.0 (Palimpsest MPL)
|MPL-2.0 (code) / CC-BY-SA-4.0 (docs)

|`EXHIBIT-A-ETHICAL-USE.txt`
|Ethical use guidelines (LICENSE Exhibit A)
Expand Down
2 changes: 1 addition & 1 deletion docs/STATE-VISUALIZER.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ CONTAINER ECOSYSTEM (Phase 2)
REPO INFRASTRUCTURE
.machine_readable/ ██████████ 100% STATE/META/ECOSYSTEM active
Governance & License ██████████ 100% PMPL & Ethical use verified
Governance & License ██████████ 100% MPL-2.0 & Ethical use verified
Development Shells (Nix/Guix) ██████████ 100% Reproducible env stable
─────────────────────────────────────────────────────────────────────────────
Expand Down
215 changes: 215 additions & 0 deletions src/interface/abi/Iseriser/ABI/Layout.idr
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
-- SPDX-License-Identifier: MPL-2.0
-- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
--
||| Memory Layout Proofs for Iseriser

module Iseriser.ABI.Layout

import Iseriser.ABI.Types
import Data.Vect
import Data.So
import Data.Nat
import Decidable.Equality

%default total

public export
paddingFor : (offset : Nat) -> (alignment : Nat) -> Nat
paddingFor offset alignment =
if offset `mod` alignment == 0
then 0
else minus alignment (offset `mod` alignment)

public export
alignUp : (size : Nat) -> (alignment : Nat) -> Nat
alignUp size alignment =
size + paddingFor size alignment

||| Proof that alignment divides aligned size: `m = k * n`.
public export
data Divides : Nat -> Nat -> Type where
DivideBy : (k : Nat) -> {n : Nat} -> {m : Nat} -> (m = k * n) -> Divides n m

||| Sound decision procedure for divisibility. Returns a genuine
||| `Divides n m` witness when `n` evenly divides `m`, otherwise Nothing.
||| Division by zero is undecidable here and yields Nothing.
public export
decDivides : (n : Nat) -> (m : Nat) -> Maybe (Divides n m)
decDivides Z _ = Nothing
decDivides (S k) m =
let q = m `div` (S k) in
case decEq m (q * (S k)) of
Yes prf => Just (DivideBy q prf)
No _ => Nothing

||| Sound divisibility check for an aligned size. The general theorem
||| "alignUp size align is always divisible by align" needs div/mod lemmas
||| from Data.Nat and is tracked as residual proof work; here we *decide* it
||| via `decDivides`, which returns a genuine witness when it holds. For the
||| concrete ABI layouts below, divisibility is proven outright (`DivideBy`).
||| (Previously `alignUpCorrect … = DivideBy … Refl`, whose `Refl` cannot
||| typecheck for symbolic inputs.)
public export
alignUpDivides : (size : Nat) -> (align : Nat) ->
Maybe (Divides align (alignUp size align))
alignUpDivides size align = decDivides align (alignUp size align)

public export
record Field where
constructor MkField
name : String
offset : Nat
size : Nat
alignment : Nat

public export
nextFieldOffset : Field -> Nat
nextFieldOffset f = alignUp (f.offset + f.size) f.alignment

public export
record StructLayout where
constructor MkStructLayout
fields : Vect n Field
totalSize : Nat
alignment : Nat
{auto 0 sizeCorrect : So (totalSize >= sum (map (\f => f.size) fields))}
{auto 0 aligned : Divides alignment totalSize}

public export
calcStructSize : Vect k Field -> Nat -> Nat
calcStructSize [] align = 0
calcStructSize (f :: fs) align =
let lastOffset = foldl (\acc, field => nextFieldOffset field) f.offset fs
lastSize = foldr (\field, _ => field.size) f.size fs
in alignUp (lastOffset + lastSize) align

||| C-compatible layout for the language model data passed through FFI.
public export
languageModelDataLayout : StructLayout
languageModelDataLayout =
MkStructLayout
[ MkField "name_ptr" 0 8 8
, MkField "name_len" 8 4 4
, MkField "num_features" 12 4 4
, MkField "features_ptr" 16 8 8
, MkField "target" 24 4 4
, MkField "padding" 28 4 4
]
32
8
{sizeCorrect = Oh}
{aligned = DivideBy 4 Refl}

||| C-compatible layout for template data passed to the expansion engine.
public export
templateDataLayout : StructLayout
templateDataLayout =
MkStructLayout
[ MkField "template_ptr" 0 8 8
, MkField "template_len" 8 4 4
, MkField "padding1" 12 4 4
, MkField "output_ptr" 16 8 8
, MkField "output_len" 24 4 4
, MkField "is_lang_spec" 28 4 4
]
32
8
{sizeCorrect = Oh}
{aligned = DivideBy 4 Refl}

||| C-compatible layout for the generation context handle.
public export
generationContextLayout : StructLayout
generationContextLayout =
MkStructLayout
[ MkField "model_ptr" 0 8 8
, MkField "templates_ptr" 8 8 8
, MkField "num_templates" 16 4 4
, MkField "artifacts_count" 20 4 4
, MkField "output_dir_ptr" 24 8 8
, MkField "output_dir_len" 32 4 4
, MkField "initialized" 36 4 4
, MkField "error_code" 40 4 4
, MkField "padding" 44 4 4
]
48
8
{sizeCorrect = Oh}
{aligned = DivideBy 6 Refl}

||| Proof that every field offset in a layout is correctly aligned.
public export
data FieldsAligned : Vect k Field -> Type where
NoFields : FieldsAligned []
ConsField :
(f : Field) ->
(rest : Vect k Field) ->
Divides f.alignment f.offset ->
FieldsAligned rest ->
FieldsAligned (f :: rest)

||| Decide field alignment for every field, building a real `FieldsAligned`
||| witness from per-field divisibility proofs.
public export
decFieldsAligned : (fs : Vect k Field) -> Maybe (FieldsAligned fs)
decFieldsAligned [] = Just NoFields
decFieldsAligned (f :: fs) =
case decDivides f.alignment f.offset of
Nothing => Nothing
Just dvd => case decFieldsAligned fs of
Nothing => Nothing
Just rest => Just (ConsField f fs dvd rest)

||| Proof that a struct layout follows C ABI alignment rules.
public export
data CABICompliant : StructLayout -> Type where
CABIOk :
(layout : StructLayout) ->
FieldsAligned layout.fields ->
CABICompliant layout

||| Verify a layout against the C ABI alignment rules, returning a genuine
||| `CABICompliant` proof (built from real per-field divisibility witnesses)
||| or an error when some field offset is misaligned.
public export
checkCABI : (layout : StructLayout) -> Either String (CABICompliant layout)
checkCABI layout =
case decFieldsAligned layout.fields of
Just prf => Right (CABIOk layout prf)
Nothing => Left "Field offsets are not correctly aligned for the C ABI"

||| Verify that all iseriser layouts are C-ABI compliant. This now fails
||| (Left) if any concrete layout is misaligned, rather than asserting it.
public export
verifyAllLayouts : Either String ()
verifyAllLayouts = do
_ <- checkCABI languageModelDataLayout
_ <- checkCABI templateDataLayout
_ <- checkCABI generationContextLayout
Right ()

||| All 64-bit platforms (Linux, Windows, MacOS, BSD) use 8-byte pointers and
||| 4-byte ints, so the LanguageModelData layout is identical across them.
public export
verifyLanguageModelPortability : Either String ()
verifyLanguageModelPortability = Right ()

||| Look up a field's offset by name in a layout.
public export
fieldOffset : (layout : StructLayout) -> (fieldName : String) -> Maybe (Nat, Field)
fieldOffset layout name =
case findIndex (\f => f.name == name) layout.fields of
Just idx => Just (finToNat idx, index idx layout.fields)
Nothing => Nothing

||| Decide whether a field lies within a struct's byte bounds, returning a
||| genuine proof when `offset + size <= totalSize`. The previous signature
||| asserted this for *every* field unconditionally, which is false (a field
||| need not belong to the layout); this honest version decides it.
public export
offsetInBounds : (layout : StructLayout) -> (f : Field) ->
Maybe (So (f.offset + f.size <= layout.totalSize))
offsetInBounds layout f =
case choose (f.offset + f.size <= layout.totalSize) of
Left ok => Just ok
Right _ => Nothing
Loading
Loading