| Author | Andy Green <andy@warmcat.com> 2026-10-03 09:24 UTC | | Committer | Andy Green <andy@warmcat.com> 2026-10-03 09:24 UTC | | Tree | 4361df462d2a7923f98c68ca3f877e56abccb11e Raw Patch | | | meta: npro workspace, its lints and the CI gates | meta: npro workspace, its lints and the CI gates
The placeholder lws crate becomes the npro workspace: members under
crates/, starting with the npro facade, which re-exports the protocol
crates as they arrive. A crate is added by the phase that gives it
content.
[workspace.lints] carries AGENTS.md's rules for every crate:
- unsafe_code forbidden, missing_docs and unexpected_cfgs denied;
- clippy denies unwrap / expect / panic / unreachable, slice indexing,
arithmetic side effects and lossy casts.
These are the C bug classes a panic or a wrapped length turns into a
remote abort or worse. clippy.toml lets only tests assert by panicking.
Cargo.lock is committed so every gate runs on the same tree.
scripts/ci.sh runs the gates every commit passes:
- fmt;
- clippy with warnings denied;
- tests;
- docs with warnings denied;
- a check at rust-version 1.85, the edition 2024 floor;
- cargo deny, against deny.toml: MIT only, one version of anything, no
unknown registries or git sources;
- cargo audit.
The README says what npro is, that it is written by AI agents working
with the author of libwebsockets, and how it is held to the C library:
transcripts, the state tables, fuzzing, differential and conformance
tests.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019kg5Eemy68ZaqDBcUJQG6J
|
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..852c0a8
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "npro"
+version = "0.0.2"
diff --git a/Cargo.toml b/Cargo.toml
index 0d76fbe..e802236 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,13 +1,36 @@
-[package]
-name = "lws"
-version = "0.0.1"
-license = "MIT"
-homepage = "https://libwebsockets.org"
-description = "Modern all-safe networking library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls"
-edition = "2024"
-readme = "README.md"
-repository = "https://libwebsockets.org/git/lws-rs"
-keywords = ["websocket", "http2", "http3", "sans-io", "networking"]
-categories = ["network-programming", "web-programming::websocket"]
+[workspace]
+resolver = "3"
+members = ["crates/*"]
+
+[workspace.package]
+version = "0.0.2"
+edition = "2024"
rust-version = "1.85"
-[dependencies]
+license = "MIT"
+homepage = "https://npro.rs"
+repository = "https://libwebsockets.org/git/npro"
+
+[workspace.lints.rust]
+unsafe_code = "forbid"
+missing_docs = "deny"
+unexpected_cfgs = "deny"
+
+# What AGENTS.md asks of every crate: nothing that can panic on input, no
+# unchecked arithmetic on lengths, no lossy integer casts. A lint is
+# silenced only by an #[allow] on the one item, with a reason.
+
+[workspace.lints.clippy]
+unwrap_used = "deny"
+expect_used = "deny"
+panic = "deny"
+unreachable = "deny"
+todo = "deny"
+unimplemented = "deny"
+indexing_slicing = "deny"
+arithmetic_side_effects = "deny"
+as_conversions = "deny"
+cast_possible_truncation = "deny"
+cast_sign_loss = "deny"
+cast_possible_wrap = "deny"
+missing_errors_doc = "deny"
+must_use_candidate = "deny"
diff --git a/README.md b/README.md
index 7c2f156..628e315 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,49 @@
-# Placeholder
+# npro
-This is a placeholder for lws by Andy Green <andy@warmcat.com>
+
+npro is the Rust port of the sansIO half of
+[libwebsockets](https://libwebsockets.org): h1, h2, h3, ws and wt as
+entirely `safe`, `no_std` state machines that take bytes and time in,
+and give bytes and events out; with an IO crate for sockets, tls
+and the event loop along side it. It's all rust.
+
+[npro Home](https://npro.rs) - [npro Git](https://npro.rs/git/npro) - [npro CI](https://npro.rs/sai)
+
+**Status: in Development** The workspace and its gates are in place,
+and the protocol crates arrive phase by phase as described in
+[docs/port-plan.md](docs/port-plan.md).
+
+## How it is written, and how it is checked
+
+npro is maintained alongside C libwebsockets. It follows the sansIO
+refactor of 16 years of libwebsockets h1, h2, h3, ws and wt using `safe`
+rust: the two abstract bodies of code are kept in sync.
+Development and auditing of lws and npro make heavy use of Fable 5.1
+but it is tightly directed by humans.
+
+It is a port of behaviour, not a transliteration of C: the C library's
+sansIO half is the specification, and the port is held to it by:
+
+- **transcripts**: byte-for-byte records of connections driven through C
+ lws with no socket and no clock of its own
+ (`minimal-examples-lowlevel/api-tests/api-test-sansio` in the C tree),
+ which npro replays;
+- **the C state tables**: every state transition C allows, from
+ `lib/sansio/wsi-state.c`, which npro's state enums must match;
+- **fuzzing**, seeded from the C library's fuzz corpora;
+- **differential and conformance testing**, against C lws and the
+ autobahn, h2spec and h3spec suites, as the IO crate makes them possible.
+
+... and the enforced design rules:
+
+- every crate is `#![forbid(unsafe_code)]`
+- nothing reachable from network data may panic
+- arithmetic on lengths is checked
+- the dependency tree stays close to empty (cargo deny + audit)
+- maximally linted via clippy to enforce code quality
+- fuzzing in CI on pushes and when CI idle
+
+## Licence
+
+MIT
diff --git a/clippy.toml b/clippy.toml
new file mode 100644
index 0000000..b519fb9
--- /dev/null
+++ b/clippy.toml
@@ -0,0 +1,5 @@
+# Tests may assert by panicking; nothing else may.
+allow-unwrap-in-tests = true
+allow-expect-in-tests = true
+allow-panic-in-tests = true
+allow-indexing-slicing-in-tests = true
diff --git a/crates/npro/Cargo.toml b/crates/npro/Cargo.toml
new file mode 100644
index 0000000..b2d1a58
--- /dev/null
+++ b/crates/npro/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "npro"
+description = "Safe sans-IO h1, h2, h3, ws and wt protocols: the Rust port of libwebsockets' sansIO half"
+readme = "../../README.md"
+keywords = ["websocket", "http2", "http3", "sans-io", "networking"]
+categories = ["network-programming", "web-programming::websocket", "no-std"]
+version.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[lints]
+workspace = true
diff --git a/crates/npro/src/lib.rs b/crates/npro/src/lib.rs
new file mode 100644
index 0000000..50d1f14
--- /dev/null
+++ b/crates/npro/src/lib.rs
@@ -0,0 +1,12 @@
+//! npro: safe, sans-IO network protocols.
+//!
+//! npro is the Rust port of the sansIO half of
+//! [libwebsockets](https://libwebsockets.org): the h1, h2, h3, ws and wt
+//! protocols as state machines that take bytes and time as input and
+//! produce bytes and events, with no sockets, threads or clock of their own.
+//!
+//! This facade re-exports the protocol crates behind features as they are
+//! written. Nothing is usable yet; see <https://npro.rs> for the status.
+
+#![no_std]
+#![forbid(unsafe_code)]
diff --git a/deny.toml b/deny.toml
new file mode 100644
index 0000000..ac49745
--- /dev/null
+++ b/deny.toml
@@ -0,0 +1,20 @@
+# cargo deny: what may enter the dependency tree. The tree is meant to stay
+# close to empty; each dependency is justified in the commit that adds it.
+
+[graph]
+all-features = true
+
+[advisories]
+yanked = "deny"
+
+[licenses]
+allow = ["MIT"]
+confidence-threshold = 0.9
+
+[bans]
+multiple-versions = "deny"
+wildcards = "deny"
+
+[sources]
+unknown-registry = "deny"
+unknown-git = "deny"
diff --git a/docs/npro.png b/docs/npro.png
new file mode 100644
index 0000000..a79cf1f
Binary files /dev/null and b/docs/npro.png differ
diff --git a/scripts/ci.sh b/scripts/ci.sh
new file mode 100755
index 0000000..3771cea
--- /dev/null
+++ b/scripts/ci.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# The gates every commit must pass, in the order that fails fastest.
+# Run from anywhere in the tree:
+#
+# scripts/ci.sh
+#
+# Set CARGO_TARGET_DIR to keep build output out of the tree. The MSRV build
+# needs the toolchain named in Cargo.toml's rust-version (rustup toolchain
+# install 1.85 --profile minimal); cargo-deny and cargo-audit must be
+# installed.
+
+set -eu
+
+cd "$(dirname "$0")/.."
+
+msrv=$(sed -n 's/^rust-version *= *"\(.*\)"/\1/p' Cargo.toml)
+
+echo "== fmt"
+cargo fmt --all --check
+
+echo "== clippy"
+cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
+
+echo "== test"
+cargo test --workspace --all-features --locked
+
+echo "== doc"
+RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps --locked
+
+echo "== msrv $msrv"
+cargo "+$msrv" check --workspace --all-targets --all-features --locked
+
+echo "== deny"
+cargo deny --all-features check
+
+echo "== audit"
+cargo audit --deny warnings
+
+echo "== all gates passed"
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index b93cf3f..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-pub fn add(left: u64, right: u64) -> u64 {
- left + right
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn it_works() {
- let result = add(2, 2);
- assert_eq!(result, 4);
- }
-}
|