Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
612695d
Add MQTT 5.0 packet framing for seven types, with proved round trip
abu-aska Oct 27, 2026
0f7b515
Add the bar file set: Spec.lean, comparator.json, bar.toml, CI workflow
abu-aska Oct 28, 2026
e22aa77
Prove the four byte cap on variable byte integers for every input
claude Oct 28, 2026
6be55f9
Refuse variable byte integers written in more bytes than needed
claude Oct 28, 2026
03cf507
Refuse property values the standard calls a Protocol Error
claude Oct 28, 2026
fa7bb2d
Refuse CONNECT will QoS and will retain bits when the will flag is clear
claude Oct 28, 2026
66d2a27
Refuse a PUBLISH with DUP set at QoS 0
claude Oct 28, 2026
fd66abf
Accept an empty PUBLISH topic name when a Topic Alias stands for it
claude Oct 28, 2026
f429c3a
Check SUBSCRIBE topic filters against the standard's filter rules
claude Oct 28, 2026
eb3381e
Keep the DISCONNECT one byte body form so it reprints as it came
claude Oct 28, 2026
104334e
Say "one example input" where a claim rests on a one-input theorem
claude Oct 28, 2026
54388ac
Record the oracle, seed, input count and failures; say "in progress"
claude Oct 28, 2026
037c209
List the new rule functions and constructors as code under proof
claude Oct 28, 2026
1a1cd52
Prove the reader sound: what it accepts is exactly a printed packet
claude Oct 28, 2026
820c1b6
Use the current names for if_pos and if_neg, so lake build is quiet
claude Oct 28, 2026
850c0a2
Align the example-input text in Spec.lean and SPEC.md with soundness
claude Oct 28, 2026
ab2b2f7
Say in the test record that the plan's third oracle was not used
claude Oct 28, 2026
2f81784
Prove that every accepted variable byte integer is the shortest writing
claude Oct 28, 2026
819b635
State each refusal rule for every input, with its text in the spec
claude Oct 28, 2026
371184b
Correct the printVBI docstring: larger values get five or more bytes
claude Oct 28, 2026
9407dc8
Say the UTF-8 codec is copied from the draft jws#2, not a jws slice
claude Oct 28, 2026
8589f71
Draw the forbidden classes from the seed as expected refusals
claude Oct 28, 2026
9f8832e
Have Paho's Properties.unpack judge the property blocks Lean reads
claude Oct 28, 2026
648a2a5
Drop the will QoS from the rules listed as not restated in the spec
claude Oct 28, 2026
7e9ef0e
Add a planted-bug script and record what it catches
claude Oct 28, 2026
e671349
Say what the shortest writing theorems cover, and pin the worked table
claude Oct 28, 2026
c5e029c
Merge main (the Apache-2.0 LICENSE) into the first slice
claude Oct 29, 2026
c61715f
Declare the Apache-2.0 package license in lakefile.toml
claude Oct 29, 2026
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
21 changes: 21 additions & 0 deletions .github/workflows/bar.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: verified bar

on:
push:
pull_request:
# Rerun when the spec reviewer approves, or an approval is dismissed.
pull_request_review:
types: [submitted, dismissed]

jobs:
bar:
# pull-requests: read lets the check confirm the spec approval on GitHub.
permissions:
contents: read
pull-requests: read
# Both lines pin the same commit of Proven-Commons/mission: bar v2, as
# proposed in its pull request #2. Move both to a release tag once
# mission publishes one.
uses: Proven-Commons/mission/.github/workflows/verified-bar.yml@6ba85c27abaaf07905c9316aa96dc29f7b5847a2
with:
bar-ref: 6ba85c27abaaf07905c9316aa96dc29f7b5847a2
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.lake/
venv/
scratch/
__pycache__/
90 changes: 90 additions & 0 deletions Main.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/-
Command line front end for the MQTT packet codec.

One request per line, in the shape `op numbers`, where the op
and the numbers are parted by one space, and the numbers are
decimal numbers parted by commas, with no spaces inside the
number list. A line holds a number in plain decimal: no sign,
no leading zeros (the number 0 itself is written `0`), and at
most twenty digits. An empty line is passed over with no
answer. A request that breaks the line shape prints `error`.

The one operation:

- `parse` takes comma parted byte numbers and prints `ok:` and
the bytes of the packet printed back when the bytes are
exactly one packet this slice reads, and `none` when they
are not. The reprint is the packet read and written again,
byte for byte when the packet arrived in its printed form.
The full rules are in SPEC.md.
-/

import Mqtt

open Mqtt

/-- Read one number token: plain decimal digits, no sign, no
leading zeros but for the number 0 itself, at most twenty
digits. -/
def parseNat (s : String) : Option Nat :=
let cs := s.toList
if cs.isEmpty ∨ 20 < cs.length then none
else if 1 < cs.length ∧ cs.head! = '0' then none
else if cs.all Char.isDigit then
some (cs.foldl (fun n c => n * 10 + (c.toNat - 48)) 0)
else none

/-- Read the number list of a payload. The empty payload is the
empty list. -/
def numsOf (payload : String) : Option (List Nat) :=
if payload.isEmpty then some []
else (payload.splitOn ",").mapM parseNat

/-- Read the byte list of a payload: every number at most 255. -/
def bytesOf (payload : String) : Option (List UInt8) :=
(numsOf payload).bind fun ns =>
if ns.all (· ≤ 255) then some (ns.map Nat.toUInt8) else none

/-- Print a number list as comma parted decimal numbers. -/
def joinNums (ns : List Nat) : String :=
ns.foldl (fun acc n => if acc.isEmpty then toString n else acc ++ "," ++ toString n) ""

/-- Run one operation on one payload. The answer is `error` for a
malformed request, and `none` for well formed bytes that this
slice rejects. -/
def run (op payload : String) : String :=
match op with
| "parse" =>
match bytesOf payload with
| none => "error"
| some bs =>
match parse bs with
| some v => "ok:" ++ joinNums ((print v).map UInt8.toNat)
| none => "none"
| _ => "error"

/-- Answer one request line. An empty line gets no answer. -/
def processLine (line : String) : Option String :=
if line.isEmpty then none
else
match line.splitOn " " with
| [op] => some (run op "")
| [op, payload] => some (run op payload)
| _ => some "error"

/-- The line without its line ending: LF, or CR LF. -/
def chomp (line : String) : String :=
let cs := line.toList
if cs.getLast? = some '\n' then
let cs := cs.dropLast
String.ofList (if cs.getLast? = some '\r' then cs.dropLast else cs)
else line

def main : IO Unit := do
let stdin ← IO.getStdin
repeat
let line ← stdin.getLine
if line.isEmpty then break
match processLine (chomp line) with
| some out => IO.println out
| none => pure ()
2 changes: 2 additions & 0 deletions Mqtt.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Mqtt.Value
import Mqtt.Parse
Loading