From 8f48b8a33afafa09317ec27354594e932b71ea83 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 27 Sep 2026 04:39:28 +0000 Subject: [PATCH] audit claims and bound repository release paths Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com> --- .clinerules | 55 +- .cursorrules | 53 +- .devcontainer/devcontainer.json | 2 +- .github/CONTRIBUTING.md | 186 +-- .github/pull_request_template.md | 65 +- .github/workflows/actions.lock | 81 +- .github/workflows/casket-pages.yml | 114 -- .github/workflows/dogfood-checks.yml | 338 ----- .github/workflows/e2e.yml | 94 -- .github/workflows/main-estate-audit.yml | 52 +- .github/workflows/pages.yml | 59 - .github/workflows/release.yml | 122 -- .../workflows/repository-static-checks.yml | 45 + .github/workflows/selected-package-tests.yml | 53 + .machine_readable/6a2/0-AI-MANIFEST.a2ml | 46 +- .machine_readable/6a2/AGENTIC.a2ml | 37 +- .machine_readable/6a2/ECOSYSTEM.a2ml | 54 +- .machine_readable/6a2/META.a2ml | 49 +- .machine_readable/6a2/NEUROSYM.a2ml | 27 +- .machine_readable/6a2/PLAYBOOK.a2ml | 51 +- .machine_readable/6a2/README.adoc | 50 +- .machine_readable/6a2/STATE.a2ml | 99 +- .../6a2/anchor/0-AI-MANIFEST.a2ml | 25 +- .machine_readable/6a2/anchor/ANCHOR.a2ml | 59 +- .machine_readable/BINDINGS.a2ml | 83 +- .machine_readable/ai/README.adoc | 30 +- .machine_readable/bot_directives/README.adoc | 45 +- .../contractiles/Adjustfile.a2ml | 85 +- .../contractiles/Intentfile.a2ml | 133 +- .machine_readable/contractiles/Justfile | 1171 +++++------------ .machine_readable/contractiles/Mustfile.a2ml | 124 +- .machine_readable/contractiles/README.adoc | 30 +- .machine_readable/contractiles/Trustfile.a2ml | 111 +- .../policies/MAINTENANCE-AXES.a2ml | 71 +- .../policies/MAINTENANCE-CHECKLIST.a2ml | 165 +-- .../SOFTWARE-DEVELOPMENT-APPROACH.a2ml | 47 +- .machine_readable/rsr-profile.a2ml | 22 + .mise.toml | 2 - .nojekyll | 0 .well-known/security.txt | 8 + .windsurfrules | 49 +- 0-AI-MANIFEST.a2ml | 264 +--- ABI-FFI-README.adoc | 417 ++---- ARCHITECTURE.adoc | 93 +- Containerfile | 45 - EXPLAINME.adoc | 164 +-- GOVERNANCE.adoc | 233 +--- Justfile | 1171 +++++------------ MAINTAINERS.adoc | 48 - PLACEHOLDERS.adoc | 237 +--- PROOF-NEEDS.adoc | 128 +- QUICKSTART-DEV.adoc | 180 +-- QUICKSTART-MAINTAINER.adoc | 184 +-- QUICKSTART-USER.adoc | 135 +- READINESS.adoc | 189 ++- README.adoc | 254 ++-- ROADMAP.adoc | 218 ++- RSR_OUTLINE.adoc | 359 +---- SECURITY.adoc | 142 +- TEST-NEEDS.adoc | 235 ++-- TOPOLOGY.adoc | 189 +-- benches/.gitkeep | 0 bindings/ada/src/proven_dns.ads | 20 +- bindings/cpp/include/proven/dns.hpp | 57 +- bindings/csharp/src/ProvenDns.cs | 48 +- bindings/go/dns.go | 34 +- .../com/hyperpolymath/proven/ProvenDns.java | 39 +- bindings/javascript/src/dns.js | 28 +- bindings/julia/src/Dns.jl | 24 +- .../com/hyperpolymath/proven/ProvenDns.kt | 28 +- bindings/lua/proven/dns.lua | 278 ++-- bindings/lua/proven/ffi.lua | 34 +- bindings/ocaml/README.adoc | 27 + bindings/ocaml/dune-project | 15 +- bindings/ocaml/lib/dune | 29 +- bindings/ocaml/lib/proven_agentic.ml | 21 +- bindings/ocaml/lib/proven_airgap.ml | 21 +- bindings/ocaml/lib/proven_amqp.ml | 21 +- bindings/ocaml/lib/proven_apiserver.ml | 21 +- bindings/ocaml/lib/proven_appserver.ml | 21 +- bindings/ocaml/lib/proven_authserver.ml | 21 +- bindings/ocaml/lib/proven_backup.ml | 21 +- bindings/ocaml/lib/proven_bfd.ml | 21 +- bindings/ocaml/lib/proven_bgp.ml | 21 +- bindings/ocaml/lib/proven_ca.ml | 21 +- bindings/ocaml/lib/proven_cache.ml | 21 +- bindings/ocaml/lib/proven_caldav.ml | 21 +- bindings/ocaml/lib/proven_carddav.ml | 21 +- bindings/ocaml/lib/proven_chat.ml | 21 +- bindings/ocaml/lib/proven_coap.ml | 21 +- bindings/ocaml/lib/proven_configmgmt.ml | 21 +- bindings/ocaml/lib/proven_container.ml | 21 +- bindings/ocaml/lib/proven_ctlog.ml | 21 +- bindings/ocaml/lib/proven_dbserver.ml | 21 +- bindings/ocaml/lib/proven_dds.ml | 21 +- bindings/ocaml/lib/proven_deception.ml | 24 +- bindings/ocaml/lib/proven_dhcp.ml | 24 +- bindings/ocaml/lib/proven_diode.ml | 24 +- bindings/ocaml/lib/proven_dns.ml | 79 +- bindings/ocaml/lib/proven_doh.ml | 21 +- bindings/ocaml/lib/proven_doq.ml | 24 +- bindings/ocaml/lib/proven_dot.ml | 24 +- bindings/ocaml/lib/proven_error.ml | 8 +- bindings/ocaml/lib/proven_federation.ml | 21 +- bindings/ocaml/lib/proven_fileserver.ml | 21 +- bindings/ocaml/lib/proven_firewall.ml | 80 +- bindings/ocaml/lib/proven_ftp.ml | 67 +- bindings/ocaml/lib/proven_gameserver.ml | 24 +- bindings/ocaml/lib/proven_git.ml | 21 +- bindings/ocaml/lib/proven_graphdb.ml | 18 +- bindings/ocaml/lib/proven_graphql.ml | 78 +- bindings/ocaml/lib/proven_grpc.ml | 75 +- bindings/ocaml/lib/proven_hardened.ml | 18 +- bindings/ocaml/lib/proven_honeypot.ml | 18 +- bindings/ocaml/lib/proven_http.ml | 18 +- bindings/ocaml/lib/proven_httpd.ml | 42 +- bindings/ocaml/lib/proven_ids.ml | 18 +- bindings/ocaml/lib/proven_imap.ml | 18 +- bindings/ocaml/lib/proven_irc.ml | 18 +- bindings/ocaml/lib/proven_kerberos.ml | 18 +- bindings/ocaml/lib/proven_kms.ml | 18 +- bindings/ocaml/lib/proven_ldap.ml | 18 +- bindings/ocaml/lib/proven_ldp.ml | 18 +- bindings/ocaml/lib/proven_loadbalancer.ml | 18 +- bindings/ocaml/lib/proven_logcollector.ml | 18 +- bindings/ocaml/lib/proven_lpd.ml | 18 +- bindings/ocaml/lib/proven_mcp.ml | 18 +- bindings/ocaml/lib/proven_mdns.ml | 18 +- bindings/ocaml/lib/proven_media.ml | 18 +- bindings/ocaml/lib/proven_metrics.ml | 18 +- bindings/ocaml/lib/proven_modbus.ml | 18 +- bindings/ocaml/lib/proven_monitor.ml | 18 +- bindings/ocaml/lib/proven_mqtt.ml | 60 +- bindings/ocaml/lib/proven_nesy.ml | 18 +- bindings/ocaml/lib/proven_netconf.ml | 18 +- bindings/ocaml/lib/proven_neurosym.ml | 18 +- bindings/ocaml/lib/proven_nfs.ml | 18 +- bindings/ocaml/lib/proven_ntp.ml | 18 +- bindings/ocaml/lib/proven_nts.ml | 18 +- bindings/ocaml/lib/proven_objectstore.ml | 18 +- bindings/ocaml/lib/proven_ocsp.ml | 18 +- bindings/ocaml/lib/proven_odns.ml | 18 +- bindings/ocaml/lib/proven_opcua.ml | 18 +- bindings/ocaml/lib/proven_ospf.ml | 18 +- bindings/ocaml/lib/proven_pop3.ml | 18 +- bindings/ocaml/lib/proven_pqc.ml | 18 +- bindings/ocaml/lib/proven_proxy.ml | 18 +- bindings/ocaml/lib/proven_ptp.ml | 18 +- bindings/ocaml/lib/proven_radius.ml | 18 +- bindings/ocaml/lib/proven_rtsp.ml | 18 +- bindings/ocaml/lib/proven_sandbox.ml | 18 +- bindings/ocaml/lib/proven_sdn.ml | 18 +- bindings/ocaml/lib/proven_semweb.ml | 18 +- bindings/ocaml/lib/proven_siem.ml | 18 +- bindings/ocaml/lib/proven_smb.ml | 18 +- bindings/ocaml/lib/proven_smtp.ml | 72 +- bindings/ocaml/lib/proven_snmp.ml | 18 +- bindings/ocaml/lib/proven_socks.ml | 18 +- bindings/ocaml/lib/proven_sparql.ml | 18 +- bindings/ocaml/lib/proven_ssh.ml | 18 +- bindings/ocaml/lib/proven_ssh_bastion.ml | 78 +- bindings/ocaml/lib/proven_stun.ml | 18 +- bindings/ocaml/lib/proven_syslog.ml | 18 +- bindings/ocaml/lib/proven_tacacs.ml | 18 +- bindings/ocaml/lib/proven_telnet.ml | 18 +- bindings/ocaml/lib/proven_tftp.ml | 18 +- bindings/ocaml/lib/proven_tls.ml | 42 +- bindings/ocaml/lib/proven_triplestore.ml | 18 +- bindings/ocaml/lib/proven_unavailable.ml | 8 + bindings/ocaml/lib/proven_virt.ml | 18 +- bindings/ocaml/lib/proven_voip.ml | 18 +- bindings/ocaml/lib/proven_vpn.ml | 18 +- bindings/ocaml/lib/proven_wasm.ml | 18 +- bindings/ocaml/lib/proven_webdav.ml | 18 +- bindings/ocaml/lib/proven_websocket.ml | 16 +- bindings/ocaml/lib/proven_xmpp.ml | 18 +- bindings/ocaml/lib/proven_zerotrust.ml | 18 +- bindings/php/src/ProvenDns.php | 57 +- bindings/python/proven_servers/dns.py | 36 +- bindings/ruby/lib/proven_servers/dns.rb | 45 +- bindings/rust/src/ffi_dns.rs | 40 +- .../Sources/ProvenServers/ProvenDns.swift | 65 +- connectors/README.adoc | 164 +-- connectors/proven-nesy-solver-api/README.adoc | 150 +-- .../generated/abi/nesy_solver_api.h | 15 +- .../proven-nesy-solver-api/zig/Containerfile | 52 +- .../proven-nesy-solver-api/zig/deploy-fly.sh | 306 +---- .../proven-nesy-solver-api/zig/fly.toml | 56 +- container/.gatekeeper.yaml | 122 -- container/0-AI-MANIFEST.a2ml | 143 -- container/Containerfile | 136 -- container/README.adoc | 179 --- container/compose.example.toml | 135 -- container/compose.toml | 70 - container/ct-build.sh | 162 --- container/deploy.k9.ncl | 176 --- container/entrypoint.sh | 63 - container/manifest.toml | 62 - container/vordr.toml | 100 -- contractile.just | 130 +- docs/AI-CONVENTIONS.adoc | 166 +-- docs/AI-INSTALL-README-SECTION.adoc | 127 +- docs/AI_INSTALLATION_GUIDE.adoc | 317 +---- docs/QUICKSTART.adoc | 104 +- docs/README.adoc | 26 +- docs/THREAT-MODEL.adoc | 385 ++---- docs/TOPOLOGY-GUIDE.adoc | 167 +-- docs/decisions/0000-template.adoc | 2 +- docs/decisions/0001-adopt-rsr-standard.adoc | 21 +- docs/decisions/README.adoc | 27 +- .../DESIGN-2026-03-01-connector-abi-ffi.adoc | 425 +----- docs/maintenance/MAINTENANCE-CHECKLIST.adoc | 810 ++---------- .../SOFTWARE-DEVELOPMENT-APPROACH.adoc | 93 +- ffi/zig/build.zig | 90 +- ffi/zig/src/main.zig | 127 +- ffi/zig/test/integration_test.zig | 209 +-- llm-warmup-dev.adoc | 34 +- llm-warmup-user.adoc | 28 +- not-proven/proven-container/src/Main.idr | 1 - .../ffi/zig/src/authserver.zig | 49 +- .../ffi/zig/test/integration_test.zig | 25 +- .../proven-authserver/proven-authserver.ipkg | 2 +- .../src/AuthserverABI/Foreign.idr | 26 +- protocols/proven-authserver/src/Main.idr | 2 +- .../proven-backup/ffi/zig/src/backup.zig | 9 +- .../ffi/zig/test/integration_test.zig | 29 +- protocols/proven-backup/proven-backup.ipkg | 2 +- .../proven-backup/src/BackupABI/Foreign.idr | 14 +- protocols/proven-backup/src/Main.idr | 1 - protocols/proven-ca/ffi/zig/src/ca.zig | 64 +- protocols/proven-ca/ffi/zig/test/ca_test.zig | 170 +-- protocols/proven-ca/proven-ca.ipkg | 2 +- protocols/proven-ca/src/CAABI/Foreign.idr | 19 +- protocols/proven-ca/src/Main.idr | 2 +- protocols/proven-ctlog/ffi/zig/src/ctlog.zig | 89 +- .../ffi/zig/test/integration_test.zig | 59 +- protocols/proven-ctlog/proven-ctlog.ipkg | 2 +- .../proven-ctlog/src/CTLogABI/Foreign.idr | 17 +- protocols/proven-ctlog/src/Main.idr | 2 +- protocols/proven-dns/ffi/zig/src/dns.zig | 133 +- .../proven-dns/ffi/zig/test/dns_test.zig | 135 +- protocols/proven-dns/generated/abi/dns.h | 6 +- protocols/proven-dns/proven-dns.ipkg | 2 +- protocols/proven-dns/src/DNS.idr | 5 +- protocols/proven-dns/src/DNSABI/Foreign.idr | 28 +- .../proven-dns/src/DNSABI/Transitions.idr | 24 +- protocols/proven-dns/src/Main.idr | 8 +- protocols/proven-graphdb/src/Main.idr | 1 - .../proven-kerberos/ffi/zig/src/kerberos.zig | 46 +- .../ffi/zig/test/kerberos_test.zig | 63 +- .../proven-kerberos/proven-kerberos.ipkg | 2 +- .../src/KerberosABI/Foreign.idr | 50 +- protocols/proven-kerberos/src/Main.idr | 2 +- protocols/proven-ldp/src/Main.idr | 1 - protocols/proven-media/src/Main.idr | 1 - protocols/proven-nesy/ffi/zig/src/nesy.zig | 27 +- .../ffi/zig/test/integration_test.zig | 8 +- protocols/proven-nesy/proven-nesy.ipkg | 2 +- protocols/proven-nesy/src/Main.idr | 2 +- protocols/proven-nesy/src/NeSyABI/Foreign.idr | 4 +- protocols/proven-odns/ffi/zig/src/odns.zig | 63 +- .../ffi/zig/test/integration_test.zig | 59 +- protocols/proven-odns/proven-odns.ipkg | 2 +- protocols/proven-odns/src/Main.idr | 2 +- protocols/proven-odns/src/ODNSABI/Foreign.idr | 18 +- protocols/proven-pqc/ffi/zig/src/pqc.zig | 70 +- .../proven-pqc/ffi/zig/test/pqc_test.zig | 34 +- protocols/proven-pqc/proven-pqc.ipkg | 2 +- protocols/proven-pqc/src/Main.idr | 2 +- protocols/proven-pqc/src/PQCABI/Foreign.idr | 8 +- .../proven-tacacs/ffi/zig/src/tacacs.zig | 11 +- .../ffi/zig/test/integration_test.zig | 22 +- .../proven-tacacs/src/TACACSABI/Foreign.idr | 32 +- protocols/proven-triplestore/src/Main.idr | 1 - protocols/proven-virt/src/Main.idr | 1 - .../ffi/zig/src/zerotrust.zig | 23 +- .../ffi/zig/test/zerotrust_test.zig | 93 +- .../proven-zerotrust/proven-zerotrust.ipkg | 2 +- protocols/proven-zerotrust/src/Main.idr | 2 +- .../src/ZeroTrustABI/Foreign.idr | 50 +- selur-compose.toml | 22 - setup.sh | 288 +--- stapeln.toml | 91 -- tests/.gitkeep | 0 tests/aspect/security_test.sh | 112 +- tests/binding_inventory.sh | 29 + tests/cross_binding_test.sh | 132 -- tests/e2e.sh | 300 +---- tests/fuzz/placeholder.txt | 1 - ...{property_test.sh => source_smoke_test.sh} | 286 ++-- tools/check-binding-policy.sh | 16 +- www/.well-known/security.txt | 7 +- 292 files changed, 6873 insertions(+), 13619 deletions(-) delete mode 100644 .github/workflows/casket-pages.yml delete mode 100644 .github/workflows/dogfood-checks.yml delete mode 100644 .github/workflows/e2e.yml delete mode 100644 .github/workflows/pages.yml delete mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/repository-static-checks.yml create mode 100644 .github/workflows/selected-package-tests.yml create mode 100644 .machine_readable/rsr-profile.a2ml delete mode 100644 .mise.toml delete mode 100644 .nojekyll create mode 100644 .well-known/security.txt delete mode 100644 Containerfile delete mode 100644 MAINTAINERS.adoc delete mode 100644 benches/.gitkeep create mode 100644 bindings/ocaml/README.adoc create mode 100644 bindings/ocaml/lib/proven_unavailable.ml delete mode 100644 container/.gatekeeper.yaml delete mode 100644 container/0-AI-MANIFEST.a2ml delete mode 100644 container/Containerfile delete mode 100644 container/README.adoc delete mode 100644 container/compose.example.toml delete mode 100644 container/compose.toml delete mode 100644 container/ct-build.sh delete mode 100644 container/deploy.k9.ncl delete mode 100644 container/entrypoint.sh delete mode 100644 container/manifest.toml delete mode 100644 container/vordr.toml delete mode 100644 selur-compose.toml delete mode 100644 stapeln.toml delete mode 100644 tests/.gitkeep create mode 100644 tests/binding_inventory.sh delete mode 100644 tests/cross_binding_test.sh delete mode 100644 tests/fuzz/placeholder.txt rename tests/{property_test.sh => source_smoke_test.sh} (54%) diff --git a/.clinerules b/.clinerules index 112dca49..23a71db7 100644 --- a/.clinerules +++ b/.clinerules @@ -1,43 +1,26 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# Authoritative source: docs/AI-CONVENTIONS.md +# Authoritative guidance: docs/AI-CONVENTIONS.adoc -# STARTUP: Read 0-AI-MANIFEST.a2ml first, then .machine_readable/STATE.a2ml. +# STARTUP +# Read 0-AI-MANIFEST.a2ml, .machine_readable/6a2/STATE.a2ml, and +# .machine_readable/6a2/anchor/ANCHOR.a2ml. -# LICENSE -# All original code: MPL-2.0. -# Never AGPL-3.0. MPL-2.0 only as platform-required fallback. -# SPDX header required on every source file. -# Copyright: Jonathan D.A. Jewell (hyperpolymath) - -# STATE FILES (.machine_readable/ ONLY) -# Never create in repo root: STATE.a2ml, META.a2ml, ECOSYSTEM.a2ml, -# AGENTIC.a2ml, NEUROSYM.a2ml, PLAYBOOK.a2ml. -# The .machine_readable/ directory is the single source of truth. - -# BANNED PATTERNS -# Idris2: believe_me, assert_total, assert_smaller, unsafePerformIO -# Haskell: unsafeCoerce, unsafePerformIO, undefined, error -# OCaml: Obj.magic, Obj.repr, Obj.obj -# Coq: Admitted -# Lean: sorry -# Rust: transmute (unless FFI with // SAFETY: comment) +# EVIDENCE +# Distinguish Idris2 model checks, Zig tests, ABI conformance, binding links, +# and protocol interoperability. Source-pattern scripts are not proofs. +# Record missing tools and explicit skips; never report an unrun check as passing. -# BANNED LANGUAGES -# TypeScript -> ReScript -# Node.js / npm / bun -> Deno -# Go -> Rust -# Python -> Julia or Rust +# FAIL-CLOSED +# Do not report authentication, crypto, or backend operations as successful +# without required secrets/material and a verified implementation. +# Do not build, sign, push, or deploy the current container scaffolding. -# CONTAINERS -# Runtime: Podman (never Docker). -# File: Containerfile (never Dockerfile). -# Base: cgr.dev/chainguard/wolfi-base:latest or cgr.dev/chainguard/static:latest. +# LANGUAGES +# Idris2 and Zig are used in selected packages. Existing binding languages are +# permitted as source inventory; their operational support is unverified. +# Do not add a new runtime/language without documenting scope and toolchains. -# ABI/FFI -# ABI: Idris2 with dependent types (src/abi/). -# FFI: Zig with C ABI (ffi/zig/). -# Headers: generated/abi/. - -# BUILD: Use just (justfile) for all tasks. -# STYLE: Descriptive names. Document all files. SPDX headers everywhere. +# LICENSE +# Preserve file-level SPDX and third-party notices. Original project source is +# generally MPL-2.0. Follow docs/AI-CONVENTIONS.adoc for details. diff --git a/.cursorrules b/.cursorrules index ec7a4fc9..1d26dde7 100644 --- a/.cursorrules +++ b/.cursorrules @@ -1,47 +1,22 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# Authoritative source: docs/AI-CONVENTIONS.md +# Authoritative guidance: docs/AI-CONVENTIONS.adoc -# Read 0-AI-MANIFEST.a2ml in the repo root FIRST for canonical file locations. +# Read 0-AI-MANIFEST.a2ml, .machine_readable/6a2/STATE.a2ml, and +# .machine_readable/6a2/anchor/ANCHOR.a2ml before editing. -# LICENSE -# All original code: MPL-2.0 (SPDX header required on every file). -# Never use AGPL-3.0. Fallback to MPL-2.0 only when platform requires it. -# Copyright: Jonathan D.A. Jewell (hyperpolymath) +# Be precise about evidence: Idris2 model builds, Zig tests, generated ABI +# checks, binding links, and protocol interoperability are different claims. +# Grep-based smoke checks are not runtime tests or formal proofs. -# STATE FILES -# .a2ml metadata files go in .machine_readable/ ONLY. -# Never create STATE.a2ml, META.a2ml, ECOSYSTEM.a2ml, AGENTIC.a2ml, -# NEUROSYM.a2ml, or PLAYBOOK.a2ml in the repository root. +# Keep unavailable authentication/cryptographic operations fail-closed. Do not +# publish or deploy the current container scaffolding. -# BANNED PATTERNS -# Idris2: believe_me, assert_total, assert_smaller, unsafePerformIO -# Haskell: unsafeCoerce, unsafePerformIO, undefined, error -# OCaml: Obj.magic, Obj.repr, Obj.obj -# Coq: Admitted -# Lean: sorry -# Rust: transmute (unless FFI with // SAFETY: comment) +# Existing language bindings are source inventory, not supported-language +# promises. Do not add a language/runtime without recording rationale and a +# reproducible toolchain plan. -# BANNED LANGUAGES -# TypeScript -> use ReScript -# Node.js / npm / bun -> use Deno -# Go -> use Rust -# Python -> use Julia or Rust +# Preserve per-file SPDX identifiers, third-party notices, and repository +# licensing rules. Original source is generally MPL-2.0. -# CONTAINERS -# Runtime: Podman (never Docker) -# File: Containerfile (never Dockerfile) -# Base: cgr.dev/chainguard/wolfi-base:latest - -# ABI/FFI STANDARD -# ABI definitions: Idris2 with dependent types (src/abi/) -# FFI implementation: Zig with C ABI (ffi/zig/) -# Generated C headers: generated/abi/ - -# BUILD SYSTEM -# Use just (justfile) for all build, test, lint, and format tasks. - -# CODE STYLE -# Use descriptive variable names. -# Annotate and document all files. -# Add SPDX-License-Identifier header to every source file. +# Use configured Justfile tasks; `fmt-check` checks Git whitespace only. diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 3218e573..7dcc6dd0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -21,7 +21,7 @@ "ghcr.io/nickel-lang/devcontainer-feature:0": {} }, - "postCreateCommand": "just deps", + "postCreateCommand": "just info", "remoteUser": "nonroot", diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 25a53f84..07a0c471 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,126 +1,76 @@ -# Clone the repository - -git clone https://github.com/hyperpolymath/proven-servers.git -cd proven-servers - -# Using Guix (recommended for reproducibility) - -guix develop - -# Or using toolbox/distrobox - -toolbox create proven-servers-dev -toolbox enter proven-servers-dev -# Install dependencies manually - -# Verify setup - -just check # or: cargo check / mix compile / etc. -just test # Run test suite - -### Repository Structure - -```text -proven-servers/ -├── src/ # Source code (Perimeter 1-2) -├── lib/ # Library code (Perimeter 1-2) -├── extensions/ # Extensions (Perimeter 2) -├── plugins/ # Plugins (Perimeter 2) -├── tools/ # Tooling (Perimeter 2) -├── docs/ # Documentation (Perimeter 3) -│ ├── architecture/ # ADRs, specs (Perimeter 2) -│ └── proposals/ # RFCs (Perimeter 3) -├── examples/ # Examples (Perimeter 3) -├── spec/ # Spec tests (Perimeter 3) -├── tests/ # Test suite (Perimeter 2-3) -├── .machine_readable/ # ALL machine-readable content (Perimeter 1) -│ ├── \*.a2ml # State files (STATE, META, ECOSYSTEM, etc.) -│ ├── bot_directives/ # Bot configs -│ └── contractiles/ # Policy contracts (k9, dust, lust, must, trust) -├── .well-known/ # Protocol files (Perimeter 1-3) -├── .github/ # GitHub config (Perimeter 1) -│ ├── CONTRIBUTING.md # This file -│ ├── ISSUE_TEMPLATE/ -│ └── workflows/ -├── CHANGELOG.md -├── CODE_OF_CONDUCT.md -├── GOVERNANCE.md -├── LICENSE -├── MAINTAINERS.md -├── README.adoc -├── SECURITY.md -├── flake.nix # Nix flake — fallback (Perimeter 1) -├── guix.scm # Guix package — primary (Perimeter 1) -└── Justfile # Task runner (Perimeter 1) -``` - - --- - -## How to Contribute - -### Reporting Bugs - - **Before reporting**: - 1. Search existing issues - 2. Check if it's already fixed in `main` - 3. Determine which perimeter the bug affects - - **When reporting**: - - Use the [bug report template](.github/ISSUE_TEMPLATE/bug_report.md) and include: - - - Clear, descriptive title - - Environment details (OS, versions, toolchain) - - Steps to reproduce - - Expected vs actual behaviour - - Logs, screenshots, or minimal reproduction - -### Suggesting Features - - **Before suggesting**: - 1. Check the [roadmap](ROADMAP.md) if available - 2. Search existing issues and discussions - 3. Consider which perimeter the feature belongs to + + - **When suggesting**: +# Contributing to proven-servers - Use the [feature request template](.github/ISSUE_TEMPLATE/feature_request.md) and include: +Thank you for helping improve this repository. It contains protocol models, +Idris2 packages, Zig FFI prototypes, language-binding sources, tests, and +maintenance material. It is **not** a production server distribution, and the +20 language-named binding directories are an inventory rather than a support +promise. - - Problem statement (what pain point does this solve?) - - Proposed solution - - Alternatives considered - - Which perimeter this affects +## Before you start -### Your First Contribution +1. Read [README.adoc](../README.adoc), [QUICKSTART-DEV.adoc](../QUICKSTART-DEV.adoc), + [AI conventions](../docs/AI-CONVENTIONS.adoc), and the package-local README. +2. Check [READINESS.adoc](../READINESS.adoc) and + [PROOF-NEEDS.adoc](../PROOF-NEEDS.adoc) for current evidence limits. +3. For security reports, follow [SECURITY.adoc](../SECURITY.adoc); do not use a + public issue or pull request to disclose a vulnerability. - Look for issues labelled: +## Development workflow - - [`good first issue`](https://github.com/hyperpolymath/proven-servers/labels/good%20first%20issue) — Simple Perimeter 3 tasks - - [`help wanted`](https://github.com/hyperpolymath/proven-servers/labels/help%20wanted) — Community help needed - - [`documentation`](https://github.com/hyperpolymath/proven-servers/labels/documentation) — Docs improvements - - [`perimeter-3`](https://github.com/hyperpolymath/proven-servers/labels/perimeter-3) — Community sandbox scope +Use a focused branch and a package-specific change. Before opening a PR: - --- - -## Development Workflow - -### Branch Naming - -docs/short-description # Documentation (P3) test/what-added # Test -additions (P3) feat/short-description # New features (P2) -fix/issue-number-description # Bug fixes (P2) refactor/what-changed # -Code improvements (P2) security/what-fixed # Security fixes (P1-2) - - -### Commit Messages - - We follow [Conventional Commits](https://www.conventionalcommits.org/): - -type(scope): description - -Body: what changed and why. - -Footer: issue reference, e.g. Closes #123 -\[optional body\] +```sh +just validate +just test-static +# When installed, run the applicable compiler checks: +just build-idris +just build-zig +just test-zig +``` -\[optional footer\] +`just test-static` runs source-pattern and inventory heuristics; it is not a +runtime test, formal proof, ABI-conformance test, or security certification. +The compiler-backed tasks require Idris2 and Zig. Record exact compiler +versions and clearly list any checks that could not run. Toolchain versions are +not yet fully pinned repository-wide. + +For changes to an individual component, also follow that package's README and +manifest. An Idris2 build validates only the definitions in the selected +`.ipkg`; a Zig test supports only the code paths it executes. Neither alone +proves that a separate header or language binding conforms. + +## Change expectations + +* Keep changes small and explain the problem and evidence in the PR. +* Preserve fail-closed behavior where credentials, key material, or a verified + backend is absent. Do not turn an unavailable security operation into a + success path. +* Do not claim bindings are wired/supported until they build, link, and run + against the intended native library. +* Update `.machine_readable/BINDINGS.a2ml`, `READINESS.adoc`, or + `PROOF-NEEDS.adoc` only when current evidence justifies the change. +* Preserve file-level SPDX identifiers and third-party license notices. +* Keep root `Justfile` synchronized with + `.machine_readable/contractiles/Justfile`. +* Do not build, sign, push, or deploy the container scaffolding; no runnable + application target is established. + +There is no repository-wide language formatter or complete multi-language lint +matrix. `just fmt-check` checks Git whitespace only; do not describe it as code +formatting. + +## Pull requests + +Use the repository PR template. Include: + +* a summary and motivation; +* affected packages and any compatibility impact; +* exact test/build commands and outcomes, with tool versions; +* explicit skipped checks and remaining risks; +* relevant issue or advisory references, when applicable. + +A green source grep or historical audit report is not evidence that modified +native code builds today. Maintainers review changes before merging. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 72f4461e..ed693e98 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,47 +1,48 @@ - + + + ## Summary - + -## Changes +## Scope and evidence - +- [ ] I reviewed the package-local README and manifest(s). +- [ ] I distinguish model checks, native tests, ABI checks, binding tests, and source-pattern smoke checks. +- [ ] I have not promoted directory counts, historical audits, or source greps into current build/conformance claims. +- [ ] Any readiness/binding claims are backed by current, reproducible evidence. -- +## Checks -## RSR Quality Checklist +List exact commands, tool versions, and outcomes. Mark unavailable checks explicitly. - +- [ ] `just validate` +- [ ] `just test-static` (heuristic/inventory checks only) +- [ ] Applicable Idris2 build(s): +- [ ] Applicable Zig build/test(s): +- [ ] Binding build/link/runtime checks, if a binding changed: +- [ ] `git diff --check` -### Required +**Skipped checks and reason:** -- [ ] Tests pass (`just test` or equivalent) -- [ ] Code is formatted (`just fmt` or equivalent) -- [ ] Linter is clean (no new warnings or errors) -- [ ] No banned language patterns (no TypeScript, no npm/bun, no Go/Python) -- [ ] No `unsafe` blocks without `// SAFETY:` comments -- [ ] No banned functions (`believe_me`, `unsafeCoerce`, `Obj.magic`, `Admitted`, `sorry`) -- [ ] SPDX license headers present on all new/modified source files -- [ ] No secrets, credentials, or `.env` files included + -### As Applicable +## Safety and compatibility -- [ ] `.machine_readable/STATE.a2ml` updated (if project state changed) -- [ ] `.machine_readable/ECOSYSTEM.a2ml` updated (if integrations changed) -- [ ] `.machine_readable/META.a2ml` updated (if architectural decisions changed) -- [ ] Documentation updated for user-facing changes -- [ ] `TOPOLOGY.md` updated (if architecture changed) -- [ ] `CHANGELOG` or release notes updated -- [ ] New dependencies reviewed for license compatibility (MPL-2.0 / MPL-2.0) -- [ ] ABI/FFI changes validated (`src/abi/` and `ffi/zig/` consistent) +- [ ] Unavailable authentication/cryptographic operations remain fail-closed. +- [ ] Native ABI declarations and buffer/length boundaries were reviewed. +- [ ] No secrets or credentials are included. +- [ ] File-level SPDX identifiers and third-party license notices are preserved. +- [ ] No container image is built, signed, pushed, or deployed by this change. -## Testing +## Documentation and metadata - +- [ ] Human-readable docs match the implementation and test scope. +- [ ] `.machine_readable/6a2/STATE.a2ml`, `META.a2ml`, or `ECOSYSTEM.a2ml` updated if relevant. +- [ ] `.machine_readable/BINDINGS.a2ml` and readiness docs updated only where new evidence supports it. +- [ ] Root `Justfile` and `.machine_readable/contractiles/Justfile` remain synchronized. +- [ ] `TOPOLOGY.adoc` or `CHANGELOG.adoc` updated if relevant. -## Screenshots +## Remaining risks / follow-up - + diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 1db85324..f1e8dc89 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -1,31 +1,23 @@ -# This file is machine-generated by `gh actions-lock`. -# Do not edit by hand; run `gh actions-lock` to update. +# This lock is normally generated by `gh actions-lock`. +# The direct workflow map, removed generic dogfood/Pages/release entries, and +# estate-action SHA pin were reconciled on 2026-09-27. The generator binary +# could not be downloaded in the assessment workspace; retained transitive +# records may include dependencies from removed workflows. # Docs: https://gh.io/actions-lockfile version: 'v0.0.2' workflows: '.github/workflows/boj-trigger.yml': - 'actions/checkout@v7.0.1' - '.github/workflows/casket-pages.yml': - - 'actions/cache@v6.1.0' - - 'actions/checkout@v7.0.1' - - 'actions/configure-pages@v6.0.0' - - 'actions/deploy-pages@v5.0.1' - - 'actions/upload-pages-artifact@v5.0.0' - - 'haskell-actions/setup@v2.12.0' '.github/workflows/codeql.yml': - 'actions/checkout@v7.0.1' - 'github/codeql-action@v4.38.0' '.github/workflows/dependabot-automerge.yml': - 'dependabot/fetch-metadata@v3.1.0' - '.github/workflows/dogfood-checks.yml': + '.github/workflows/repository-static-checks.yml': - 'actions/checkout@v7.0.1' - - 'hyperpolymath/k9-ecosystem@main' - '.github/workflows/e2e.yml': + '.github/workflows/selected-package-tests.yml': - 'actions/checkout@v7.0.1' - - 'actions/upload-artifact@v7.0.1' - - 'dtolnay/rust-toolchain@stable' - 'goto-bus-stop/setup-zig@v2.2.1' - - 'swatinem/rust-cache@v2.9.2' '.github/workflows/governance.yml': [] '.github/workflows/hypatia-scan.yml': - 'actions/checkout@v7.0.1' @@ -39,12 +31,8 @@ workflows: '.github/workflows/labels.yml': [] '.github/workflows/main-estate-audit.yml': - 'actions/checkout@v7.0.1' - - 'hyperpolymath/cicd-suite@main' + - 'hyperpolymath/cicd-suite@5a10b72e574ef63855fafd4568a4c178657f3294' '.github/workflows/mirror.yml': [] - '.github/workflows/pages.yml': - - 'actions/checkout@v7.0.1' - - 'actions/deploy-pages@v5.0.1' - - 'actions/upload-pages-artifact@v5.0.0' '.github/workflows/panic-attack-unified-api-adapter.yml': - 'actions/checkout@v7.0.1' - 'actions/upload-artifact@v7.0.1' @@ -52,55 +40,24 @@ workflows: - 'swatinem/rust-cache@v2.9.2' '.github/workflows/push-email-notify.yml': - 'hyperpolymath/smtp-notify-action@v0.3.0' - '.github/workflows/release.yml': - - 'actions/checkout@v7.0.1' - - 'actions/upload-artifact@v7.0.1' - - 'softprops/action-gh-release@v3.0.3' '.github/workflows/scorecard.yml': [] '.github/workflows/secret-scanner.yml': [] dependencies: - 'actions/cache@v6.1.0': - ref: 'v6.1.0' - commit: 'sha1-55cc8345863c7cc4c66a329aec7e433d2d1c52a9' - owner_id: 44036562 - repo_id: 215566462 'actions/checkout@v7.0.1': ref: 'v7.0.1' commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' owner_id: 44036562 repo_id: 197814629 - 'actions/configure-pages@v6.0.0': - ref: 'v6.0.0' - commit: 'sha1-45bfe0192ca1faeb007ade9deae92b16b8254a0d' - owner_id: 44036562 - repo_id: 513659658 - 'actions/deploy-pages@v5.0.1': - ref: 'v5.0.1' - commit: 'sha1-368f82528645a54fb793d4d04e342629a3f51346' - owner_id: 44036562 - repo_id: 438112499 'actions/github-script@v9.0.0': ref: 'v9.0.0' commit: 'sha1-3a2844b7e9c422d3c10d287c895573f7108da1b3' owner_id: 44036562 repo_id: 205262760 - 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f': - ref: 'v7.0.0' - commit: 'sha1-bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' - owner_id: 44036562 - repo_id: 192625955 'actions/upload-artifact@v7.0.1': ref: 'v7.0.1' commit: 'sha1-043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' owner_id: 44036562 repo_id: 192625955 - 'actions/upload-pages-artifact@v5.0.0': - ref: 'v5.0.0' - commit: 'sha1-fc324d3547104276b827a68afc52ff2a11cc49c9' - owner_id: 44036562 - repo_id: 496012378 - uses: - - 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' 'dependabot/fetch-metadata@v3.1.0': ref: 'v3.1.0' commit: 'sha1-25dd0e34f4fe68f24cc83900b1fe3fe149efef98' @@ -126,12 +83,7 @@ dependencies: commit: 'sha1-abea47f85e598557f500fa1fd2ab7464fcb39406' owner_id: 1006268 repo_id: 212984112 - 'haskell-actions/setup@v2.12.0': - ref: 'v2.12.0' - commit: 'sha1-6037f33647c3f17758a2356c80fc4a53d7e0685d' - owner_id: 75048950 - repo_id: 623796603 - 'hyperpolymath/cicd-suite@main': + 'hyperpolymath/cicd-suite@5a10b72e574ef63855fafd4568a4c178657f3294': ref: 'main' commit: 'sha1-5a10b72e574ef63855fafd4568a4c178657f3294' owner_id: 6759885 @@ -149,11 +101,6 @@ dependencies: commit: 'sha1-2155aa26a21758f2ba119f61bc7e0e1981c106fb' owner_id: 6759885 repo_id: 1275650185 - 'hyperpolymath/k9-ecosystem@main': - ref: 'main' - commit: 'sha1-c1a34884054fabf0e9de81dbf68f4ba7874e85f1' - owner_id: 6759885 - repo_id: 1275650185 'hyperpolymath/smtp-notify-action@v0.3.0': ref: 'v0.3.0' commit: 'sha1-22e7bdb322c430c1d0dac6b3bb307f4bb139d0be' @@ -164,21 +111,11 @@ dependencies: commit: 'sha1-28959ce8df70de7be546dd1250a005dd32156697' owner_id: 18365890 repo_id: 220359305 - 'softprops/action-gh-release@v3.0.3': - ref: 'v3.0.3' - commit: 'sha1-efb35369e0ad2afab669f228072c1b0d510eae64' - owner_id: 2242 - repo_id: 204253808 'swatinem/rust-cache@v2.9.2': ref: 'v2.9.2' commit: 'sha1-6323deb102c322ba6fcbdcafc7e3dddab59af2b6' owner_id: 580492 repo_id: 298565987 - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9': - ref: 'v6.1.0' - commit: 'sha1-55cc8345863c7cc4c66a329aec7e433d2d1c52a9' - owner_id: 44036562 - repo_id: 215566462 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1': ref: 'v7.0.1' commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1' diff --git a/.github/workflows/casket-pages.yml b/.github/workflows/casket-pages.yml deleted file mode 100644 index 3fb13fa7..00000000 --- a/.github/workflows/casket-pages.yml +++ /dev/null @@ -1,114 +0,0 @@ -# This workflow is managed by gh actions-lock. -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -# This workflow is managed by gh actions-lock. -# This workflow is managed by gh actions-lock. -name: GitHub Pages -on: - push: - branches: [main, master] - workflow_dispatch: -permissions: - actions: read - contents: read - pages: write - id-token: write -concurrency: - group: "pages" - cancel-in-progress: false -jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout - uses: actions/checkout@v7.0.1 - - name: Checkout casket-ssg - uses: actions/checkout@v7.0.1 - with: - repository: hyperpolymath/casket-ssg - path: .casket-ssg - - name: Setup GHCup - uses: haskell-actions/setup@v2.12.0 - with: - ghc-version: '9.8.2' - cabal-version: '3.10' - - name: Cache Cabal - uses: actions/cache@v6.1.0 - with: - path: | - ~/.cabal/packages - ~/.cabal/store - .casket-ssg/dist-newstyle - key: ${{ runner.os }}-casket-${{ hashFiles('.casket-ssg/casket-ssg.cabal') }} - - name: Build casket-ssg - working-directory: .casket-ssg - run: cabal build - - name: Prepare site source - shell: bash - env: - REPO_NAME: ${{ github.event.repository.name }} - REPO_FULL_NAME: ${{ github.repository }} - BRANCH_NAME: ${{ github.ref_name }} - run: | - set -euo pipefail - rm -rf .site-src _site - - if [ -d site ]; then - cp -R site .site-src - else - mkdir -p .site-src - TODAY="$(date +%Y-%m-%d)" - REPO_NAME="${REPO_NAME}" - REPO_URL="https://github.com/${REPO_FULL_NAME}" - README_URL="" - - if [ -f README.md ]; then - README_URL="${REPO_URL}/blob/${BRANCH_NAME}/README.md" - elif [ -f README.adoc ]; then - README_URL="${REPO_URL}/blob/${BRANCH_NAME}/README.adoc" - fi - - { - echo "---" - echo "title: ${REPO_NAME}" - echo "date: ${TODAY}" - echo "---" - echo - echo "# ${REPO_NAME}" - echo - echo "Static documentation site for ${REPO_NAME}." - echo - echo "- Source repository: [${REPO_FULL_NAME}](${REPO_URL})" - if [ -n "${README_URL}" ]; then - echo "- README: [project README](${README_URL})" - fi - if [ -d docs ]; then - echo "- Docs directory: [docs/](${REPO_URL}/tree/${BRANCH_NAME}/docs)" - fi - echo - echo "Project-specific site content can be added later under site/." - } > .site-src/index.md - fi - - name: Build site - run: | - mkdir -p _site - cd .casket-ssg && cabal run casket-ssg -- build ../.site-src ../_site - touch ../_site/.nojekyll - - name: Setup Pages - uses: actions/configure-pages@v6.0.0 - - name: Upload artifact - uses: actions/upload-pages-artifact@v5.0.0 - with: - path: '_site' - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - timeout-minutes: 15 - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v5.0.1 diff --git a/.github/workflows/dogfood-checks.yml b/.github/workflows/dogfood-checks.yml deleted file mode 100644 index 356dc915..00000000 --- a/.github/workflows/dogfood-checks.yml +++ /dev/null @@ -1,338 +0,0 @@ -# This workflow is managed by gh actions-lock. -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -# This workflow is managed by gh actions-lock. -# This workflow is managed by gh actions-lock. -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# -# dogfood-gate.yml — Hyperpolymath Dogfooding Quality Gate -# Validates that the repo uses hyperpolymath's own formats and tools. -# Companion to static-analysis-gate.yml (security) — this is for format compliance. -name: Dogfood Gate - -on: - pull_request: - branches: ['**'] - push: - branches: [main, master] - -permissions: - actions: read - contents: read - -jobs: - # --------------------------------------------------------------------------- - # Job 2: K9 contract validation - # --------------------------------------------------------------------------- - k9-validate: - name: Validate K9 contracts - runs-on: ubuntu-latest - timeout-minutes: 15 - - steps: - - name: Checkout repository - uses: actions/checkout@v7.0.1 - - - name: Check for K9 files - id: detect - run: | - COUNT=$(find . \( -name '*.k9' -o -name '*.k9.ncl' \) -not -path './.git/*' | wc -l) - CONFIG_COUNT=$(find . \( -name '*.toml' -o -name '*.yaml' -o -name '*.yml' -o -name '*.json' \) \ - -not -path './.git/*' -not -path './node_modules/*' -not -path './.deno/*' \ - -not -name 'package-lock.json' -not -name 'Cargo.lock' -not -name 'deno.lock' | wc -l) - echo "k9_count=$COUNT" >> "$GITHUB_OUTPUT" - echo "config_count=$CONFIG_COUNT" >> "$GITHUB_OUTPUT" - if [ "$COUNT" -eq 0 ] && [ "$CONFIG_COUNT" -gt 0 ]; then - echo "::warning::Found $CONFIG_COUNT config files but no K9 contracts. Run k9iser to generate contracts." - fi - - - name: Validate K9 contracts - if: steps.detect.outputs.k9_count > 0 - uses: hyperpolymath/k9-ecosystem/validate-action@main - with: - path: '.' - strict: 'false' - - - name: Write summary - run: | - K9_COUNT="${{ steps.detect.outputs.k9_count }}" - CFG_COUNT="${{ steps.detect.outputs.config_count }}" - if [ "$K9_COUNT" -eq 0 ]; then - cat <<'EOF' >> "$GITHUB_STEP_SUMMARY" - ## K9 Contract Validation - - :warning: **No K9 contract files found.** Repos with configuration files should have K9 contracts. - - Generate contracts with: `k9iser generate .` - EOF - else - echo "## K9 Contract Validation" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "Validated **${K9_COUNT}** K9 contract(s) against **${CFG_COUNT}** config file(s)." >> "$GITHUB_STEP_SUMMARY" - fi - - # --------------------------------------------------------------------------- - # Job 3: Empty-linter — invisible character detection - # --------------------------------------------------------------------------- - empty-lint: - name: Empty-linter (invisible characters) - runs-on: ubuntu-latest - timeout-minutes: 15 - - steps: - - name: Checkout repository - uses: actions/checkout@v7.0.1 - - - name: Scan for invisible characters - id: lint - run: | - # Inline invisible character detection (from empty-linter's core patterns). - # Checks for: zero-width spaces, zero-width joiners, BOM, soft hyphens, - # non-breaking spaces, null bytes, and other invisible Unicode in source files. - set +e - PATTERNS='\xc2\xa0|\xe2\x80\x8b|\xe2\x80\x8c|\xe2\x80\x8d|\xef\xbb\xbf|\xc2\xad|\xe2\x80\x8e|\xe2\x80\x8f|\xe2\x80\xaa|\xe2\x80\xab|\xe2\x80\xac|\xe2\x80\xad|\xe2\x80\xae|\x00' - find "$GITHUB_WORKSPACE" \ - -not -path '*/.git/*' -not -path '*/node_modules/*' \ - -not -path '*/.deno/*' -not -path '*/target/*' \ - -not -path '*/_build/*' -not -path '*/deps/*' \ - -not -path '*/external_corpora/*' -not -path '*/.lake/*' \ - -type f \( -name '*.rs' -o -name '*.ex' -o -name '*.exs' -o -name '*.res' \ - -o -name '*.js' -o -name '*.ts' -o -name '*.json' -o -name '*.toml' \ - -o -name '*.yml' -o -name '*.yaml' -o -name '*.md' -o -name '*.adoc' \ - -o -name '*.idr' -o -name '*.zig' -o -name '*.v' -o -name '*.jl' \ - -o -name '*.gleam' -o -name '*.hs' -o -name '*.ml' -o -name '*.sh' \) \ - -exec grep -Prl "$PATTERNS" {} \; > /tmp/empty-lint-results.txt 2>/dev/null - EL_EXIT=$? - set -e - - FINDINGS=$(wc -l < /tmp/empty-lint-results.txt 2>/dev/null || echo 0) - echo "findings=$FINDINGS" >> "$GITHUB_OUTPUT" - echo "exit_code=$EL_EXIT" >> "$GITHUB_OUTPUT" - echo "ready=true" >> "$GITHUB_OUTPUT" - - # Emit annotations for each file with invisible chars - while IFS= read -r filepath; do - [ -z "$filepath" ] && continue - REL_PATH="${filepath#$GITHUB_WORKSPACE/}" - echo "::warning file=${REL_PATH}::Invisible Unicode characters detected (zero-width space, BOM, NBSP, etc.)" - done < /tmp/empty-lint-results.txt - - - name: Write summary - run: | - if [ "${{ steps.lint.outputs.ready }}" = "true" ]; then - FINDINGS="${{ steps.lint.outputs.findings }}" - if [ "$FINDINGS" -gt 0 ] 2>/dev/null; then - echo "## Empty-Linter Results" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "Found **${FINDINGS}** invisible character issue(s). See annotations above." >> "$GITHUB_STEP_SUMMARY" - else - echo "## Empty-Linter Results" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo ":white_check_mark: No invisible character issues found." >> "$GITHUB_STEP_SUMMARY" - fi - else - echo "## Empty-Linter" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "Skipped: empty-linter not available." >> "$GITHUB_STEP_SUMMARY" - fi - - # --------------------------------------------------------------------------- - # Job 4: Groove manifest check (for repos that should expose services) - # --------------------------------------------------------------------------- - groove-check: - name: Groove manifest check - runs-on: ubuntu-latest - timeout-minutes: 15 - - steps: - - name: Checkout repository - uses: actions/checkout@v7.0.1 - - - name: Check for Groove manifest - id: groove - run: | - # Check for static or dynamic Groove endpoints - HAS_MANIFEST="false" - HAS_GROOVE_CODE="false" - - if [ -f ".well-known/groove/manifest.json" ]; then - HAS_MANIFEST="true" - # Validate the manifest JSON - if ! jq empty .well-known/groove/manifest.json 2>/dev/null; then - echo "::error file=.well-known/groove/manifest.json::Invalid JSON in Groove manifest" - else - SVC_ID=$(jq -r '.service_id // "unknown"' .well-known/groove/manifest.json) - echo "service_id=$SVC_ID" >> "$GITHUB_OUTPUT" - fi - fi - - # Check for Groove endpoint code (Rust, Elixir, Zig, V) - if grep -rl 'well-known/groove' --include='*.rs' --include='*.ex' --include='*.zig' --include='*.v' --include='*.res' . 2>/dev/null | head -1 | grep -q .; then - HAS_GROOVE_CODE="true" - fi - - # Check if this repo likely serves HTTP (has server/listener code) - HAS_SERVER="false" - if grep -rl 'TcpListener\|Bandit\|Plug.Cowboy\|httpz\|vweb\|axum::serve\|actix_web' --include='*.rs' --include='*.ex' --include='*.zig' --include='*.v' . 2>/dev/null | head -1 | grep -q .; then - HAS_SERVER="true" - fi - - echo "has_manifest=$HAS_MANIFEST" >> "$GITHUB_OUTPUT" - echo "has_groove_code=$HAS_GROOVE_CODE" >> "$GITHUB_OUTPUT" - echo "has_server=$HAS_SERVER" >> "$GITHUB_OUTPUT" - - if [ "$HAS_SERVER" = "true" ] && [ "$HAS_MANIFEST" = "false" ] && [ "$HAS_GROOVE_CODE" = "false" ]; then - echo "::warning::This repo has server code but no Groove endpoint. Add .well-known/groove/manifest.json for service discovery." - fi - - - name: Write summary - run: | - echo "## Groove Protocol Check" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "| Check | Status |" >> "$GITHUB_STEP_SUMMARY" - echo "|-------|--------|" >> "$GITHUB_STEP_SUMMARY" - echo "| Static manifest (.well-known/groove/manifest.json) | ${{ steps.groove.outputs.has_manifest }} |" >> "$GITHUB_STEP_SUMMARY" - echo "| Groove endpoint in code | ${{ steps.groove.outputs.has_groove_code }} |" >> "$GITHUB_STEP_SUMMARY" - echo "| Has HTTP server code | ${{ steps.groove.outputs.has_server }} |" >> "$GITHUB_STEP_SUMMARY" - - # --------------------------------------------------------------------------- - # Job 5: eclexiaiser manifest validation - # --------------------------------------------------------------------------- - eclexiaiser-validate: - name: Validate eclexiaiser manifest - runs-on: ubuntu-latest - timeout-minutes: 15 - - steps: - - name: Checkout repository - uses: actions/checkout@v7.0.1 - - - name: Check and validate eclexiaiser manifest - id: eclex - run: | - if [ ! -f "eclexiaiser.toml" ]; then - # Check if repo has a Containerfile — if so, recommend eclexiaiser - if [ -f "Containerfile" ]; then - echo "::warning::Containerfile present but no eclexiaiser.toml. Run \`eclexiaiser init\` to scaffold energy/carbon budgets." - fi - echo "has_manifest=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - echo "has_manifest=true" >> "$GITHUB_OUTPUT" - - # Validate TOML structure using Python 3.11+ tomllib - python3 -c " - import tomllib, sys - with open('eclexiaiser.toml', 'rb') as f: - data = tomllib.load(f) - project = data.get('project', {}) - if not project.get('name', '').strip(): - print('ERROR: project.name is required', file=sys.stderr) - sys.exit(1) - functions = data.get('functions', []) - if not functions: - print('ERROR: at least one [[functions]] entry is required', file=sys.stderr) - sys.exit(1) - for fn in functions: - if not fn.get('name', '').strip(): - print('ERROR: function name cannot be empty', file=sys.stderr) - sys.exit(1) - if not fn.get('source', '').strip(): - print(f'ERROR: function {fn[\"name\"]} has no source path', file=sys.stderr) - sys.exit(1) - print(f'Valid: {project[\"name\"]} ({len(functions)} function(s))') - " || { - echo "::error file=eclexiaiser.toml::Invalid eclexiaiser.toml — see step output for details" - exit 1 - } - - - name: Write summary - run: | - if [ "${{ steps.eclex.outputs.has_manifest }}" = "true" ]; then - echo "## Eclexiaiser Manifest" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo ":white_check_mark: **eclexiaiser.toml** present and valid." >> "$GITHUB_STEP_SUMMARY" - else - echo "## Eclexiaiser Manifest" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo ":ballot_box_with_check: No eclexiaiser.toml. Add one with \`eclexiaiser init\` for energy/carbon tracking." >> "$GITHUB_STEP_SUMMARY" - fi - - # --------------------------------------------------------------------------- - # Job 6: Dogfooding summary - # --------------------------------------------------------------------------- - dogfood-summary: - name: Dogfooding compliance summary - runs-on: ubuntu-latest - timeout-minutes: 15 - needs: [k9-validate, empty-lint, groove-check, eclexiaiser-validate] - if: always() - - steps: - - name: Checkout repository - uses: actions/checkout@v7.0.1 - - - name: Generate dogfooding scorecard - run: | - SCORE=0 - MAX=5 - - # K9 contracts present? - if find . \( -name '*.k9' -o -name '*.k9.ncl' \) -not -path './.git/*' | head -1 | grep -q .; then - SCORE=$((SCORE + 1)) - K9_STATUS=":white_check_mark:" - else - K9_STATUS=":x:" - fi - - # .editorconfig present? - if [ -f ".editorconfig" ]; then - SCORE=$((SCORE + 1)) - EC_STATUS=":white_check_mark:" - else - EC_STATUS=":x:" - fi - - # Groove manifest or code? - if [ -f ".well-known/groove/manifest.json" ] || grep -rl 'well-known/groove' --include='*.rs' --include='*.ex' --include='*.zig' . 2>/dev/null | head -1 | grep -q .; then - SCORE=$((SCORE + 1)) - GROOVE_STATUS=":white_check_mark:" - else - GROOVE_STATUS=":ballot_box_with_check:" - fi - - # VeriSimDB integration? - if grep -rl 'verisimdb\|VeriSimDB' --include='*.toml' --include='*.yaml' --include='*.yml' --include='*.json' --include='*.rs' --include='*.ex' . 2>/dev/null | head -1 | grep -q .; then - SCORE=$((SCORE + 1)) - VSDB_STATUS=":white_check_mark:" - else - VSDB_STATUS=":ballot_box_with_check:" - fi - - # eclexiaiser energy tracking? - if [ -f "eclexiaiser.toml" ]; then - SCORE=$((SCORE + 1)) - ECLEX_STATUS=":white_check_mark:" - else - ECLEX_STATUS=":ballot_box_with_check:" - fi - - cat <> "$GITHUB_STEP_SUMMARY" - ## Dogfooding Scorecard - - **Score: ${SCORE}/${MAX}** - - | Tool/Format | Status | Notes | - |-------------|--------|-------| - | K9 contracts | ${K9_STATUS} | Required for repos with config files | - | .editorconfig | ${EC_STATUS} | Required for all repos | - | Groove endpoint | ${GROOVE_STATUS} | Required for service repos | - | VeriSimDB integration | ${VSDB_STATUS} | Required for stateful repos | - | eclexiaiser | ${ECLEX_STATUS} | Energy/carbon budgets for container services | - - --- - *Generated by the [Dogfood Gate](https://github.com/hyperpolymath/rsr-template-repo) workflow.* - *Dogfooding is guinea pig fooding — we test our tools on ourselves.* - EOF - diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml deleted file mode 100644 index 9d052788..00000000 --- a/.github/workflows/e2e.yml +++ /dev/null @@ -1,94 +0,0 @@ -# This workflow is managed by gh actions-lock. -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -# This workflow is managed by gh actions-lock. -# This workflow is managed by gh actions-lock. -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# -# proven-servers — E2E + Safety + Bench -# -# Tests ABI/FFI round-trip across 6 connectors, 84 protocols, and 8 core -# primitives. Safety aspects enforce no dangerous patterns. - -name: E2E + Safety + Bench -on: - push: - branches: [main, master, develop] - paths: - - 'connectors/**' - - 'protocols/**' - - 'core/**' - - 'bindings/**' - - 'tests/**' - - 'tools/**' - - '.github/workflows/e2e.yml' - pull_request: - branches: [main, master] - workflow_dispatch: -permissions: - actions: read -concurrency: - group: e2e-${{ github.ref }} - cancel-in-progress: true -jobs: - e2e: - name: E2E — Connector + Protocol FFI Round-Trip - runs-on: ubuntu-latest - timeout-minutes: 45 - steps: - - name: Checkout - uses: actions/checkout@v7.0.1 - - name: Install Zig - uses: goto-bus-stop/setup-zig@v2.2.1 - with: - version: 0.15.1 - # Builds the proven Idris ABI/engine so e2e.sh Section 0 can verify the - # proofs and that the generated header/Zig constants still match them. - # From-source (pinned v0.7.0) is the standard Linux install; if a runner - # needs a different method, e2e.sh Section 0 skips the Idris steps - # gracefully and the Zig comptime guard still runs. - - name: Install Idris2 0.7.0 - # Best-effort: a from-source build can vary by runner. If it fails, - # e2e.sh Section 0 skips the Idris steps and the Zig comptime guard - # still runs -- so the conformance check degrades gracefully rather - # than blocking the whole E2E job at this step. - continue-on-error: true - run: | - sudo apt-get update - sudo apt-get install -y chezscheme libgmp-dev - git clone --depth 1 --branch v0.7.0 https://github.com/idris-lang/Idris2.git "$RUNNER_TEMP/Idris2" - make -C "$RUNNER_TEMP/Idris2" bootstrap SCHEME=scheme - make -C "$RUNNER_TEMP/Idris2" install - echo "$HOME/.idris2/bin" >> "$GITHUB_PATH" - "$HOME/.idris2/bin/idris2" --version - - name: Run E2E test suite - run: bash tests/e2e.sh - benchmarks: - name: Bench — Rust Binding Performance - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout - uses: actions/checkout@v7.0.1 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - - name: Rust cache - uses: Swatinem/rust-cache@v2.9.2 - with: - workspaces: bindings/rust - - name: Run benchmarks - run: | - if [ -f "bindings/rust/Cargo.toml" ]; then - cargo bench --manifest-path bindings/rust/Cargo.toml 2>&1 | tee /tmp/bench-results.txt - else - echo "No Rust bindings Cargo.toml found" - fi - - name: Upload benchmark results - if: always() - uses: actions/upload-artifact@v7.0.1 - with: - name: benchmark-results - path: /tmp/bench-results.txt - retention-days: 30 diff --git a/.github/workflows/main-estate-audit.yml b/.github/workflows/main-estate-audit.yml index 2229d944..33d8b40e 100644 --- a/.github/workflows/main-estate-audit.yml +++ b/.github/workflows/main-estate-audit.yml @@ -17,79 +17,79 @@ jobs: - uses: actions/checkout@v7.0.1 - name: Required Files Gate - uses: hyperpolymath/cicd-suite/actions/required-files-check@main + uses: hyperpolymath/cicd-suite/actions/required-files-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Code Hygiene Gate - uses: hyperpolymath/cicd-suite/actions/code-hygiene-check@main + uses: hyperpolymath/cicd-suite/actions/code-hygiene-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Manifest Validation Gate - uses: hyperpolymath/cicd-suite/actions/manifest-check@main + uses: hyperpolymath/cicd-suite/actions/manifest-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Idris2 ABI Purity Gate - uses: hyperpolymath/cicd-suite/actions/idris2-abi-check@main + uses: hyperpolymath/cicd-suite/actions/idris2-abi-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Zig UnifiedApiAdapter API Gate - uses: hyperpolymath/cicd-suite/actions/zig-unified-api-adapter-check@main + uses: hyperpolymath/cicd-suite/actions/zig-unified-api-adapter-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Contractile Validation Gate - uses: hyperpolymath/cicd-suite/actions/contractile-validation-check@main + uses: hyperpolymath/cicd-suite/actions/contractile-validation-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Recipes Set Validation Gate - uses: hyperpolymath/cicd-suite/actions/recipes-set-check@main + uses: hyperpolymath/cicd-suite/actions/recipes-set-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Affirmation Document Gate - uses: hyperpolymath/cicd-suite/actions/affirmation-check@main + uses: hyperpolymath/cicd-suite/actions/affirmation-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Academic Referencing Gate - uses: hyperpolymath/cicd-suite/actions/referencing-check@main + uses: hyperpolymath/cicd-suite/actions/referencing-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Semantic Audit Gate - uses: hyperpolymath/cicd-suite/actions/semantic-audit-check@main + uses: hyperpolymath/cicd-suite/actions/semantic-audit-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: SPDX License Gate - uses: hyperpolymath/cicd-suite/actions/spdx-license-check@main + uses: hyperpolymath/cicd-suite/actions/spdx-license-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Proof Runner Gate - uses: hyperpolymath/cicd-suite/actions/proof-runner-check@main + uses: hyperpolymath/cicd-suite/actions/proof-runner-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: PRAT Testing Gate - uses: hyperpolymath/cicd-suite/actions/prat-check@main + uses: hyperpolymath/cicd-suite/actions/prat-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Panic Attack & Pons Gate - uses: hyperpolymath/cicd-suite/actions/custom-tools-check@main + uses: hyperpolymath/cicd-suite/actions/custom-tools-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: WWW & Well-Known Compliance Gate - uses: hyperpolymath/cicd-suite/actions/www-compliance-check@main + uses: hyperpolymath/cicd-suite/actions/www-compliance-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: BoJ Cartridge Validation Gate - uses: hyperpolymath/cicd-suite/actions/boj-cartridge-check@main + uses: hyperpolymath/cicd-suite/actions/boj-cartridge-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Formatting Validation Gate - uses: hyperpolymath/cicd-suite/actions/formatting-check@main + uses: hyperpolymath/cicd-suite/actions/formatting-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Accreditations & Badges Gate - uses: hyperpolymath/cicd-suite/actions/badges-check@main + uses: hyperpolymath/cicd-suite/actions/badges-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Metrics Extraction Gate - uses: hyperpolymath/cicd-suite/actions/metrics-check@main + uses: hyperpolymath/cicd-suite/actions/metrics-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Linguist & Banned Languages Gate - uses: hyperpolymath/cicd-suite/actions/linguist-check@main + uses: hyperpolymath/cicd-suite/actions/linguist-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Test & Benchmarks Dashboard Gate - uses: hyperpolymath/cicd-suite/actions/tests-benches-check@main + uses: hyperpolymath/cicd-suite/actions/tests-benches-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Hosting & Site Status Gate - uses: hyperpolymath/cicd-suite/actions/hosting-check@main + uses: hyperpolymath/cicd-suite/actions/hosting-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Git-Sea Analytics Gate - uses: hyperpolymath/cicd-suite/actions/gitsea-check@main + uses: hyperpolymath/cicd-suite/actions/gitsea-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Trust & Humans Validation Gate - uses: hyperpolymath/cicd-suite/actions/trust-humans-check@main + uses: hyperpolymath/cicd-suite/actions/trust-humans-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Are We UnAPI Gate (Secret Scanning) - uses: hyperpolymath/cicd-suite/actions/secrets-check@main + uses: hyperpolymath/cicd-suite/actions/secrets-check@5a10b72e574ef63855fafd4568a4c178657f3294 - name: Reasonably Good Token Validation Gate - uses: hyperpolymath/cicd-suite/actions/vaulted-tokens-check@main + uses: hyperpolymath/cicd-suite/actions/vaulted-tokens-check@5a10b72e574ef63855fafd4568a4c178657f3294 diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml deleted file mode 100644 index b72eb746..00000000 --- a/.github/workflows/pages.yml +++ /dev/null @@ -1,59 +0,0 @@ -# This workflow is managed by gh actions-lock. -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -# This workflow is managed by gh actions-lock. -# This workflow is managed by gh actions-lock. -name: GitHub Pages (Ddraig SSG) -on: - push: - branches: [main, master] - workflow_dispatch: -permissions: - actions: read - contents: read - pages: write - id-token: write -concurrency: - group: "pages" - cancel-in-progress: false -jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 15 - container: - image: ghcr.io/stefan-hoeck/idris2-pack@sha256:f0758996a931fb35d9ecb1de273c4d59dabe2a09b433afc7e357f65a08b7e1ff - steps: - - name: Checkout Site - uses: actions/checkout@v7.0.1 - - name: Checkout Ddraig SSG - uses: actions/checkout@v7.0.1 - with: - repository: hyperpolymath/ddraig-ssg - path: .ddraig-ssg - - name: Compile Ddraig - working-directory: .ddraig-ssg - run: idris2 Ddraig.idr -o ddraig - - name: Build site - run: | - mkdir -p src - if [ ! -f src/index.md ] && [ -f README.md ]; then - cp README.md src/index.md - elif [ ! -f src/index.md ]; then - echo "# ${GITHUB_REPOSITORY}" > src/index.md - fi - ./.ddraig-ssg/build/exec/ddraig build src _site https://hyperpolymath.github.io/${GITHUB_REPOSITORY#*/} - - name: Upload artifact - uses: actions/upload-pages-artifact@v5.0.0 - with: - path: '_site' - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - timeout-minutes: 15 - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v5.0.1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index b3a1406f..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,122 +0,0 @@ -# This workflow is managed by gh actions-lock. -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -# This workflow is managed by gh actions-lock. -# This workflow is managed by gh actions-lock. -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# -# Release workflow — triggered by version tags (v*). -# Builds artifacts, generates changelog via git-cliff, creates a GitHub Release, -# and produces SLSA provenance attestations. -name: Release -on: - push: - tags: - - 'v*' -permissions: - actions: read - contents: read -jobs: - build: - name: Build Artifacts - runs-on: ubuntu-latest - timeout-minutes: 15 - permissions: - contents: read - steps: - - uses: actions/checkout@v7.0.1 - - name: Build - run: | - echo "Build your artifacts here" - # TODO: Replace with your build commands - # Examples: - # cargo build --release - # zig build -Doptimize=ReleaseFast - # gleam build - # mix release - # TODO: Upload build artifacts if needed - # - uses: actions/upload-artifact@ea165f8d65b6db9a8b71b5c2d1a090c0daf9c8bb # v4 - # with: - # name: release-artifacts - # path: target/release/ - changelog: - name: Generate Changelog - runs-on: ubuntu-latest - timeout-minutes: 15 - permissions: - contents: read - outputs: - changelog: ${{ steps.cliff.outputs.content }} - version: ${{ steps.version.outputs.version }} - steps: - - uses: actions/checkout@v7.0.1 - with: - fetch-depth: 0 - - name: Extract version from tag - id: version - run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" - - name: Install git-cliff - run: | - curl -sSfL https://github.com/orhun/git-cliff/releases/latest/download/git-cliff-$(uname -m)-unknown-linux-gnu.tar.gz \ - | tar -xz --strip-components=1 -C /usr/local/bin/ git-cliff-*/git-cliff - - name: Generate changelog for this release - id: cliff - run: | - # Generate changelog for the current tag only - CHANGELOG=$(git cliff --latest --strip header) - # Write to output using delimiter to handle multiline - { - echo "content<> "$GITHUB_OUTPUT" - - name: Update full CHANGELOG.md - run: | - git cliff --output CHANGELOG.md - - name: Upload updated CHANGELOG.md - uses: actions/upload-artifact@v7.0.1 - with: - name: changelog - path: CHANGELOG.md - retention-days: 5 - release: - name: Create GitHub Release - needs: [build, changelog] - runs-on: ubuntu-latest - timeout-minutes: 15 - permissions: - contents: write - steps: - - uses: actions/checkout@v7.0.1 - # TODO: Download build artifacts if uploading to the release - # - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - # with: - # name: release-artifacts - # path: artifacts/ - - name: Create GitHub Release - uses: softprops/action-gh-release@v3.0.3 - with: - body: ${{ needs.changelog.outputs.changelog }} - draft: false - prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-alpha') }} - generate_release_notes: false - # TODO: Add artifact files to the release - # files: | - # artifacts/* - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - provenance: - name: SLSA Provenance - needs: [build] - permissions: - actions: read - id-token: write - contents: write - # SLSA generator must run in a separate, isolated workflow - # See: https://slsa.dev/spec/v1.0/requirements#build-l3 - uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@f7dd8c54c2067bafc12ca7a55595d5ee9b75204a # v2.1.0 - with: - base64-subjects: "" - # TODO: Replace with actual artifact hashes - # Generate with: sha256sum artifact | base64 -w0 - # base64-subjects: "${{ needs.build.outputs.hashes }}" diff --git a/.github/workflows/repository-static-checks.yml b/.github/workflows/repository-static-checks.yml new file mode 100644 index 00000000..40774efa --- /dev/null +++ b/.github/workflows/repository-static-checks.yml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# Run repository-local shell and source-policy smoke checks. These checks do +# not compile or execute protocol implementations and are not proof evidence. +name: Repository Static Checks + +on: + pull_request: + branches: ['**'] + push: + branches: [main, master] + workflow_dispatch: + +permissions: + contents: read + +jobs: + static-checks: + name: Repository-local static checks + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout repository + uses: actions/checkout@v7.0.1 + + - name: Check shell syntax + shell: bash + run: | + set -euo pipefail + while IFS= read -r -d '' script; do + bash -n "$script" + done < <(find . -path './.git' -prune -o -type f -name '*.sh' -print0) + + - name: Run selected source-pattern smoke checks + run: bash tests/source_smoke_test.sh + + - name: Run selected security-source heuristics + run: bash tests/aspect/security_test.sh + + - name: Check binding inventory and source policy + run: bash tests/binding_inventory.sh + + - name: Check synchronized Justfiles + run: cmp -s Justfile .machine_readable/contractiles/Justfile diff --git a/.github/workflows/selected-package-tests.yml b/.github/workflows/selected-package-tests.yml new file mode 100644 index 00000000..e595d10c --- /dev/null +++ b/.github/workflows/selected-package-tests.yml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# This runs a bounded selected-package build/test sweep. It is not a complete +# repository matrix, a cross-language conformance suite, or a benchmark job. + +name: Selected Package Tests +on: + push: + branches: [main, master, develop] + paths: + - 'connectors/**' + - 'protocols/**' + - 'core/**' + - 'bindings/**' + - 'tests/**' + - 'tools/**' + - 'Justfile' + - '.github/workflows/selected-package-tests.yml' + - '.github/workflows/repository-static-checks.yml' + - '.github/workflows/actions.lock' + - '.machine_readable/rsr-profile.a2ml' + pull_request: + branches: [main, master] + workflow_dispatch: +permissions: + contents: read +concurrency: + group: selected-package-tests-${{ github.ref }} + cancel-in-progress: true +jobs: + selected-package-tests: + name: Selected Idris2 and Zig package tests + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - name: Checkout + uses: actions/checkout@v7.0.1 + - name: Install Zig 0.15.2 + uses: goto-bus-stop/setup-zig@v2.2.1 + with: + version: 0.15.2 + - name: Install Idris2 build prerequisites + run: sudo apt-get update && sudo apt-get install -y chezscheme libgmp-dev + - name: Install Idris2 0.7.0 + run: | + git clone --depth 1 --branch v0.7.0 https://github.com/idris-lang/Idris2.git "$RUNNER_TEMP/Idris2" + make -C "$RUNNER_TEMP/Idris2" bootstrap SCHEME=scheme + make -C "$RUNNER_TEMP/Idris2" install + echo "$HOME/.idris2/bin" >> "$GITHUB_PATH" + "$HOME/.idris2/bin/idris2" --version + - name: Run selected package tests + run: bash tests/e2e.sh diff --git a/.machine_readable/6a2/0-AI-MANIFEST.a2ml b/.machine_readable/6a2/0-AI-MANIFEST.a2ml index 6bf1f8c7..1839ce61 100644 --- a/.machine_readable/6a2/0-AI-MANIFEST.a2ml +++ b/.machine_readable/6a2/0-AI-MANIFEST.a2ml @@ -1,31 +1,33 @@ -# AI Manifest for 6a2 Directory += AI Manifest: 6a2 metadata -## Purpose +:toc: -This manifest declares the AI-assistant context for the 6a2 machine-readable metadata directory. +== Purpose -## Canonical Locations +This file describes the repository's six core A2ML metadata documents. It is +supporting guidance; `.machine_readable/6a2/` is the canonical location. -The 6 core A2ML files MUST exist in this directory: -1. AGENTIC.a2ml -2. ECOSYSTEM.a2ml -3. META.a2ml -4. NEUROSYM.a2ml -5. PLAYBOOK.a2ml -6. STATE.a2ml +== Core Metadata -## Invariants +The following files live directly in `.machine_readable/6a2/`: -- No duplicate files in root directory -- Single source of truth: this directory is authoritative -- No stale metadata +* `AGENTIC.a2ml` — agent scope, safety, and evidence constraints +* `ECOSYSTEM.a2ml` — repository role and integration evidence boundary +* `META.a2ml` — architecture and development metadata +* `NEUROSYM.a2ml` — status of automated/symbolic analysis +* `PLAYBOOK.a2ml` — current maintenance and verification runbook +* `STATE.a2ml` — current project status and evidence -## Protocol +The canonical anchor is stored separately at +`.machine_readable/6a2/anchor/ANCHOR.a2ml`. -When multiple agents may write to A2ML files concurrently: -1. Read file and record git-sha-at-read in [provenance] section -2. Lock by creating .lock- -3. Write updated file with new [provenance] metadata -4. Release by removing lock file -5. On conflict: re-read and retry if git-sha-at-read does not match HEAD +== Editing Rules +* Keep exactly one authoritative copy of each core metadata document. +* Update `last-updated` when the document's substantive facts change. +* Keep claims consistent with source, reproducible commands, and the human + readiness/proof-needs documentation. +* Record build results with the exact revision, tool versions, command, and + outcome; a source scan or historical report is not current verification. +* Do not create ad-hoc locks or provenance sections unless a repository tool + defines and validates that protocol. diff --git a/.machine_readable/6a2/AGENTIC.a2ml b/.machine_readable/6a2/AGENTIC.a2ml index 08b29c09..43500921 100644 --- a/.machine_readable/6a2/AGENTIC.a2ml +++ b/.machine_readable/6a2/AGENTIC.a2ml @@ -1,12 +1,11 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# AGENTIC.a2ml — AI agent constraints and capabilities -# Defines what AI agents can and cannot do in this repository. +# AGENTIC.a2ml — AI-agent scope, evidence, and safety constraints. [metadata] -version = "0.1.0" -last-updated = "2026-03-02" +version = "0.2.0" +last-updated = "2026-09-27" [agent-permissions] can-edit-source = true @@ -14,14 +13,22 @@ can-edit-tests = true can-edit-docs = true can-edit-config = true can-create-files = true +external-actions-require-approval = true [agent-constraints] -# What AI agents must NOT do: -# - Never use banned language patterns (believe_me, unsafeCoerce, etc.) -# - Never commit secrets or credentials -# - Never use banned languages (TypeScript, Python, Go, etc.) -# - Never place state files in repository root (must be in .machine_readable/) -# - Never use AGPL license (use MPL-2.0) +never-commit-secrets = true +never-claim-build-or-test-success-without-running-the-current-command = true +never-equate-source-grep-or-type-level-models-with-runtime-conformance = true +never-enable-authentication-or-cryptographic-success-without-required-material-and-verified-backend = true +never-enable-container-publishing-until-a-deployable-server-target-is-validated = true +never-create-duplicate-core-state-files-at-repository-root = true + +[language-policy] +model-and-abi-specification = "Idris2 where present" +native-ffi = "Zig where present" +existing-bindings = "Language-named directories are source inventory; do not infer operational support." +new-language = "Do not add a new implementation language or runtime without recording scope and toolchain rationale." +forbidden-proof-escape-hatches = ["believe_me", "assert_total", "assert_smaller", "unsafeCoerce", "Obj.magic", "Admitted", "sorry"] [maintenance-integrity] fail-closed = true @@ -30,7 +37,11 @@ allow-silent-skip = false require-rerun-after-fix = true release-claim-requires-hard-pass = true +[tooling-boundary] +required-for-full-package-checks = ["just", "idris2", "zig"] +formatter = "No repository-wide formatter is configured; do not report fmt-check as formatting." +static-tests = "Report as source-pattern/inventory smoke checks only." + [automation-hooks] -# on-enter: Read 0-AI-MANIFEST.a2ml, then STATE.a2ml -# on-exit: Update STATE.a2ml with session outcomes -# on-commit: Run just validate-rsr +on-enter = ["Read 0-AI-MANIFEST.a2ml", "Read .machine_readable/6a2/STATE.a2ml", "Read .machine_readable/6a2/anchor/ANCHOR.a2ml"] +on-exit = ["Record completed work, test outcomes, skips, and remaining risks in the handoff"] diff --git a/.machine_readable/6a2/ECOSYSTEM.a2ml b/.machine_readable/6a2/ECOSYSTEM.a2ml index 344d3bb8..73ffb598 100644 --- a/.machine_readable/6a2/ECOSYSTEM.a2ml +++ b/.machine_readable/6a2/ECOSYSTEM.a2ml @@ -1,45 +1,39 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# ECOSYSTEM.a2ml — Ecosystem position for proven-servers. +# ECOSYSTEM.a2ml — repository position and integration evidence boundary. [metadata] -version = "0.3.0" -last-updated = "2026-04-11" +version = "0.4.0" +last-updated = "2026-09-27" [project] name = "proven-servers" -purpose = "Formally verified server component catalog — protocol types, core primitives, and connector interfaces with dependent-type proofs. ABI in Idris2, FFI in Zig, consumable from any language." -role = "ffi-infrastructure" +purpose = "Source monorepo of protocol models, FFI prototypes, binding sources, tests, and documentation." +role = "research-and-prototype-source" +not-a = ["production server distribution", "verified runtime integration bundle"] [position-in-ecosystem] -tier = "infrastructure" +tier = "infrastructure-source" +status = "unreleased; runtime integrations not established by this checkout review" [related-projects] projects = [ - { name = "panll", relationship = "dependent", notes = "VAB (Verified Assembly Building) panel consumes the component catalog for visual server composition" }, - { name = "proven (original)", relationship = "predecessor", notes = "Replaced — original had 4566 believe_me instances and bulk AI-generated bindings. proven-servers is a clean rewrite with zero believe_me." }, - { name = "language-bridges", relationship = "sibling-standard", notes = "Shares the Idris2-ABI / Zig-FFI / C-header pattern" }, - { name = "rsr-template-repo", relationship = "template-source", notes = "Repository scaffolding, workflows, and governance structure sourced from RSR template" }, - { name = "reasonably-good-token-vault", relationship = "active-integration", notes = "RGTV vault-broker deployed as rgtv-nesy on Fly.io; nesy-solver-api fetches NESY_INGEST_TOKEN via grant→redeem protocol. RGTV_URL + RGTV_AGENT_TOKEN configured in fly.toml." }, - { name = "echidna", relationship = "active-integration", notes = "Prover dispatcher consumed by nesy-solver-api as echidna-nesy.flycast:8090; cold-start hardening applied (90s grace, HTTP health check)." }, - { name = "verisimdb", relationship = "active-integration", notes = "Scan results, build metrics, and dependency graph emitted via hexad format. See integrations/verisimdb.a2ml." }, - { name = "hypatia", relationship = "planned-integration", notes = "v1.2.0 milestone — Hypatia CI scan + feedback loop + automated ABI compliance check. See ROADMAP.adoc." }, - { name = "stapeln", relationship = "potential-consumer", notes = "Container orchestration could use proven-servers for verified network stacks" }, - { name = "gitbot-fleet", relationship = "potential-consumer", notes = "Automated maintenance bots could manage connector version updates" }, - { name = "conative-gating", relationship = "planned-integration", notes = "v1.1.0 milestone — consent-aware HTTP gateway layer for nesy-solver-api." }, - { name = "lithoglyph", relationship = "planned-integration", notes = "v1.3.0 milestone — VeriSimDB→Lithoglyph aqueduct (4-stage: sanitise → structure → lithoglyph_insert → verisim_ingest)." }, - { name = "cadre-tea-router", relationship = "planned-integration", notes = "v1.4.0 milestone — ReScript TEA pattern refactor of echidna's 33 UI components." }, + { name = "rsr-template-repo", relationship = "template-source", notes = "Provides repository structure and governance patterns; project-specific applicability is verified against actual paths." }, + { name = "standards", relationship = "policy-source", notes = "Provides estate standards and capability applicability policy; draft specifications are not treated as ratified requirements." }, + { name = "panll", relationship = "documented-association", notes = "Historical machine metadata references PanLL; no current runtime integration was verified." }, + { name = "reasonably-good-token-vault", relationship = "documented-association", notes = "Historical machine metadata references RGTV; no current runtime integration was verified." }, + { name = "echidna", relationship = "documented-association", notes = "Historical machine metadata references echidna; no current runtime integration was verified." }, + { name = "verisimdb", relationship = "documented-association", notes = "Historical machine metadata references VeriSimDB; no current runtime integration was verified." }, ] -[integration-points] -points = [ - { system = "PanLL VAB", direction = "outbound", protocol = "static catalog data (108 components)" }, - { system = "C ABI consumers", direction = "outbound", protocol = "libproven_*.so / libproven_*.a via standard C calling convention" }, - { system = "Idris2 compiler", direction = "inbound", protocol = "dependent-type verification at compile time" }, - { system = "Zig build system", direction = "inbound", protocol = "zig build / zig build test" }, - { system = "RGTV (rgtv-nesy)", direction = "inbound", protocol = "HTTP grant→redeem over Fly.io private 6PN (.flycast)" }, - { system = "echidna (echidna-nesy)", direction = "outbound", protocol = "HTTP POST /api/verify over Fly.io private 6PN (.flycast)" }, - { system = "verisim-api", direction = "outbound", protocol = "HTTP POST /attempts over Fly.io private 6PN (.flycast)" }, - { system = "panic-attack", direction = "inbound", protocol = "unified-api-adapter 16-surface CI scan via panic-attack-unified-api-adapter.yml" }, -] +[integration-evidence] +verified-runtime-integrations = [] +abi-ffi-status = "Package-specific; see BINDINGS.a2ml, READINESS.adoc, and PROOF-NEEDS.adoc." +external-service-claims = "Historical metadata is not deployment or connectivity evidence." + +[tooling] +task-runner = "Justfile" +model-compiler = "Idris2" +native-compiler = "Zig" +version-policy = "Supported versions and reproducible build matrix still need to be recorded." diff --git a/.machine_readable/6a2/META.a2ml b/.machine_readable/6a2/META.a2ml index c3c8fdad..a50eb8b8 100644 --- a/.machine_readable/6a2/META.a2ml +++ b/.machine_readable/6a2/META.a2ml @@ -1,34 +1,35 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# META.a2ml — Meta-level information for proven-servers. +# META.a2ml — architecture and development metadata for proven-servers. [metadata] -version = "0.2.0" -last-updated = "2026-03-01" +version = "0.3.0" +last-updated = "2026-09-27" [project-info] -type = "library" -languages = ["idris2", "zig", "c"] +type = "source-monorepo" +languages = ["idris2", "zig", "c", "ada", "cpp", "csharp", "dart", "elixir", "gleam", "go", "haskell", "java", "javascript", "julia", "kotlin", "lua", "ocaml", "php", "python", "rescript", "ruby", "rust", "swift"] license = "MPL-2.0" author = "Jonathan D.A. Jewell (hyperpolymath)" +release-status = "unreleased; not a production server distribution" [architecture-decisions] decisions = [ - { id = "ADR-001", title = "Use Idris2 for ABI definitions", status = "accepted", date = "2026-01-30", rationale = "Dependent types prove tag encodings, state machines, and capability witnesses at compile time. No other language can express these invariants." }, - { id = "ADR-002", title = "Use Zig for FFI implementation", status = "accepted", date = "2026-01-30", rationale = "Native C ABI compatibility, cross-compilation, no runtime dependencies, memory-safe by default." }, - { id = "ADR-003", title = "Zero believe_me policy", status = "accepted", date = "2026-03-01", rationale = "The original proven repo had 4566 believe_me instances. proven-servers is a clean rewrite with zero. All proofs are genuine." }, - { id = "ADR-004", title = "Error tag 0 = no error convention", status = "accepted", date = "2026-03-01", rationale = "Matches C convention where 0 indicates success. All connector error enums reserve tag 0 for the no-error case." }, - { id = "ADR-005", title = "Per-connector state machines", status = "accepted", date = "2026-03-01", rationale = "Each connector has a domain-specific lifecycle (auth has 6 states, cache has 4, etc.) rather than a shared generic state machine. This allows proofs specific to each domain." }, - { id = "ADR-006", title = "Connectors are interface-only", status = "accepted", date = "2026-03-01", rationale = "Connector Zig FFI implementations are skeleton state machines, not full backend drivers. They prove the interface contract; real backends plug in via the C ABI." }, + { id = "ADR-001", title = "Use Idris2 for selected model and ABI definitions", status = "accepted", date = "2026-01-30", rationale = "Dependent types express selected protocol tags, state transitions, and model-level propositions; a successful package build proves only the checked Idris2 definitions." }, + { id = "ADR-002", title = "Use Zig for selected native FFI prototypes", status = "accepted", date = "2026-01-30", rationale = "Zig can export C-callable functions, but it does not by itself establish memory safety, ABI correspondence, or production readiness." }, + { id = "ADR-003", title = "Do not use proof escape hatches to claim verification", status = "accepted", date = "2026-03-01", rationale = "Source scans are heuristic and limited; no repository-wide compiler-backed proof claim is made until the exact package builds are reproduced." }, + { id = "ADR-004", title = "Bindings are not considered operational from source declarations alone", status = "accepted", date = "2026-09-27", rationale = "Native linking and executable boundary tests are required before a binding is described as wired or supported." }, + { id = "ADR-005", title = "Keep unavailable security operations fail closed", status = "accepted", date = "2026-09-27", rationale = "Authentication, DNSSEC, CA signing/validation, and PQC operations without required secrets, challenge bytes, or cryptographic backends must not report success." }, + { id = "ADR-006", title = "Treat container and deployment files as disabled scaffolding", status = "accepted", date = "2026-09-27", rationale = "The repository does not currently establish a deployable server executable, health endpoint, or validated runtime image." }, ] [development-practices] -build-tool = "just" -container-runtime = "podman" -ci-platform = "github-actions" -package-manager = "zig" -abi-tool = "idris2" +build-tool = "Justfile for configured package and smoke-check tasks" +model-language = "Idris2" +native-ffi-language = "Zig" +ci-platform = "GitHub Actions; coverage must be checked per workflow" +formatting = "No repository-wide language formatter is configured" [maintenance-axes] scoping-first = true @@ -37,14 +38,16 @@ axis-1 = "must > intend > like" axis-2 = "corrective > adaptive > perfective" axis-3 = "systems > compliance > effects" -[scoping] -sources = "README, roadmap, status docs, maintenance checklist, CI/security docs" -marker-scan = "TODO/FIXME/XXX/HACK/STUB/PARTIAL" -idris-unsound-scan = "believe_me/assert_total" +[verification-boundary] +current-status = "No current-checkout compiler/build proof is recorded." +idris2 = "Proves only propositions included in a successfully built package manifest." +zig = "Tests support only executed code paths; source-pattern checks are not runtime tests." +abi-ffi = "No repository-wide Idris2/Zig/header/binding conformance is established." +readiness = "See READINESS.adoc and PROOF-NEEDS.adoc; historical audit reports are revision-specific." [design-rationale] notes = [ - "proven-servers exists because the original 'proven' repo was bulk AI-generated with 4566 believe_me escape hatches. This is the honest rewrite.", - "The 108-component catalog is intentionally minimal: only types, state machines, and proofs. No framework code, no HTTP handling, no database drivers. Just the verified bones.", - "Connectors are the integration surface — they are built first because they define how verified server cores talk to external infrastructure.", + "Directory counts are inventory and do not imply implementation, support, or readiness.", + "Some package operations intentionally reject requests until credentials, cryptographic material, or compatible native bridges exist.", + "The root FFI example is a prototype and does not claim Idris ABI conformance.", ] diff --git a/.machine_readable/6a2/NEUROSYM.a2ml b/.machine_readable/6a2/NEUROSYM.a2ml index d0e398d8..0d980af9 100644 --- a/.machine_readable/6a2/NEUROSYM.a2ml +++ b/.machine_readable/6a2/NEUROSYM.a2ml @@ -1,23 +1,24 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# NEUROSYM.a2ml — Neurosymbolic integration metadata -# Configuration for Hypatia scanning and symbolic reasoning. +# NEUROSYM.a2ml — status of automated and symbolic analysis integration. [metadata] -version = "0.1.0" -last-updated = "2026-03-02" +version = "0.2.0" +last-updated = "2026-09-27" -[hypatia-config] -scan-enabled = true -scan-depth = "standard" # quick | standard | deep -report-format = "logtalk" +[automated-analysis] +hypatia-status = "not verified as an active local or CI check in this assessment" +panic-attack-status = "optional external scanner; not installed in the assessment workspace" +report-format = "No repository-wide machine-consumed analysis report format is configured." [symbolic-rules] -# Custom symbolic rules for this project -# - { name = "no-unsafe-ffi", pattern = "believe_me|unsafeCoerce", severity = "critical" } +active-project-rules = [] +source-scan-note = "A textual occurrence scan is heuristic; comments and generated files require review." [neural-config] -# Neural pattern detection settings -# confidence-threshold = 0.85 -# model = "hypatia-v2" +model = "none configured" +confidence-threshold = "not applicable" + +[limitations] +formal-proof-status = "No neural or heuristic result substitutes for a successful Idris2 package build or executable test." diff --git a/.machine_readable/6a2/PLAYBOOK.a2ml b/.machine_readable/6a2/PLAYBOOK.a2ml index e5045e99..24502cae 100644 --- a/.machine_readable/6a2/PLAYBOOK.a2ml +++ b/.machine_readable/6a2/PLAYBOOK.a2ml @@ -1,35 +1,40 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# PLAYBOOK.a2ml — Operational playbook -# Runbooks, incident response, deployment procedures. +# PLAYBOOK.a2ml — current local maintenance and evidence runbook. [metadata] -version = "0.1.0" -last-updated = "2026-03-02" +version = "0.2.0" +last-updated = "2026-09-27" -[deployment] -# method = "gitops" # gitops | manual | ci-triggered -# target = "container" # container | binary | library | wasm +[development] +entrypoint = "QUICKSTART-DEV.adoc" +task-runner = "Justfile" +package-specific-instructions = "Read the package README, .ipkg, and build.zig before running a package command." + +[verification] +source-smoke = "just test-static" +model-builds = "just build-idris" +native-builds = "just build-zig" +native-tests = "just test-zig" +quality = "just quality" +missing-tool-behavior = "The configured build/test tasks fail explicitly when the required compiler is absent." [incident-response] -# 1. Check .machine_readable/STATE.a2ml for current status -# 2. Review recent commits and CI results -# 3. Run `just validate` to check compliance -# 4. Run `just security` to audit for vulnerabilities +steps = [ + "Read READINESS.adoc and PROOF-NEEDS.adoc for current evidence boundaries.", + "Identify the exact package, source revision, and toolchain versions.", + "Run the package-specific build and executable tests; preserve complete logs.", + "Do not publish or deploy container artifacts; deployment is disabled.", + "Update STATE.a2ml and the corresponding human-readable status only when evidence changes.", +] [release-process] -# 1. Update version in STATE.a2ml, META.a2ml, Justfile -# 2. Run `just release-preflight` (validate + quality + security + maint-hard-pass) -# 3. Optional local permission hardening: `just perms-snapshot && just perms-lock` -# 4. Tag and push -# 5. Restore local permissions if needed: `just perms-restore` -# 6. Run `just container-push` if applicable +status = "No production release process is configured for this source monorepo." +forbidden-until-validated = ["release-tagging", "container-signing", "container-pushing", "deployment"] [maintenance-operations] -# Baseline audit: -# just maint-audit -# Hard release gate: -# just maint-hard-pass -# Permission audit: -# just perms-audit +metadata-validation = "just validate" +static-smoke = "just test-static" +full-quality = "just quality (requires Just, Idris2, and Zig)" +rollback = "Use reviewed, path-scoped Git operations; no automatic working-tree rollback recipe is provided." diff --git a/.machine_readable/6a2/README.adoc b/.machine_readable/6a2/README.adoc index 916a7020..ea0b711f 100644 --- a/.machine_readable/6a2/README.adoc +++ b/.machine_readable/6a2/README.adoc @@ -1,30 +1,22 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -# A2ML 6a2 Directory - -This directory contains the 6 core A2ML machine-readable metadata files for this repository. - -## Files - -- `AGENTIC.a2ml` - AI agent operational gating, safety controls -- `ECOSYSTEM.a2ml` - Project ecosystem position, relationships, explicit boundaries -- `META.a2ml` - Architecture decisions (ADRs), development practices, design rationale -- `NEUROSYM.a2ml` - Symbolic semantics, composition algebra -- `PLAYBOOK.a2ml` - Executable plans, operational runbooks -- `STATE.a2ml` - Project state, phase, milestones, session history - -## Standards Compliance - -These files follow the A2ML Format Family specification from: -https://github.com/hyperpolymath/standards/tree/main/a2ml - -## Generation - -These files may be generated from .scm source files using transpilation tools. -Source .scm files should be removed after successful transpilation. - -## See Also - -- [A2ML Repository Template](https://github.com/hyperpolymath/standards/blob/main/A2ML-REPO-TEMPLATE.adoc) -- [6A2 Format Family](https://github.com/hyperpolymath/standards#a2ml-format-family-7-formats) - +// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) += 6a2 Metadata Directory +:toc: + +This directory contains the six core A2ML metadata documents for this +repository: + +* `AGENTIC.a2ml` — agent scope, safety, and evidence constraints +* `ECOSYSTEM.a2ml` — repository position and integration evidence boundary +* `META.a2ml` — architecture and development metadata +* `NEUROSYM.a2ml` — automated/symbolic analysis status +* `PLAYBOOK.a2ml` — maintenance and verification runbook +* `STATE.a2ml` — current project status + +The repository anchor is stored under `anchor/ANCHOR.a2ml`. Additional +registries, policies, contractiles, integrations, and bot directives are kept +in their named sibling directories under `.machine_readable/`. + +A2ML metadata is maintained as source text. No generation or validation tool +is configured here to prove its syntax or semantic consistency; the Justfile's +metadata checks validate selected required paths and fields only. diff --git a/.machine_readable/6a2/STATE.a2ml b/.machine_readable/6a2/STATE.a2ml index 64c7c155..7cf4a2f4 100644 --- a/.machine_readable/6a2/STATE.a2ml +++ b/.machine_readable/6a2/STATE.a2ml @@ -1,78 +1,71 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# STATE.a2ml — Project state checkpoint for proven-servers. +# STATE.a2ml — current project status for proven-servers. [metadata] project = "proven-servers" -version = "0.4.0" -last-updated = "2026-04-11" +version = "unreleased" +last-updated = "2026-09-27" status = "active" [project-context] -name = "proven-servers" -purpose = "Catalog of 108 formally verified server components (94 protocol skeletons, 8 core primitives, 6 connector interfaces) in Idris2 with dependent types. ABI defined in Idris2, FFI implemented in Zig, callable from any language via C ABI." -completion-percentage = 65 +purpose = "Source monorepo of protocol models, Idris2 packages, Zig FFI prototypes, binding sources, and audit/documentation material. It is not a collection of production-ready servers." +protocol-directories = 88 +core-directories = 5 +connector-directories = 6 +binding-directories = 20 +counts-are = "directory inventory only; not build, support, or readiness evidence" [position] -phase = "implementation" -maturity = "alpha" +phase = "prototype-verification" +maturity = "research-prototype" +readiness-grade = "X" -[route-to-mvp] -milestones = [ - { name = "Phase 0: 94 protocol skeletons (Types.idr for each)", completion = 100 }, - { name = "Phase 0b: 8 core primitives (socket, frame, fsm, wire, compose, tls, config, audit)", completion = 100 }, - { name = "Phase 0c: 6 connector interface skeletons (Types.idr for each)", completion = 100 }, - { name = "Phase 1: Connector ABI-FFI (Idris2 + C headers + Zig + tests)", completion = 100 }, - { name = "Phase 2: Core primitive ABI-FFI", completion = 0 }, - { name = "Phase 3: Protocol ABI-FFI (high-priority: dns, httpd, tls, smtp)", completion = 0 }, - { name = "Phase 4: Language bindings (Rust, ReScript, Gleam, Elixir)", completion = 50 }, - { name = "Phase 5: CI/CD (Idris2 type-checking, Zig builds, cross-platform)", completion = 0 }, - { name = "Phase 6: PanLL VAB panel integration", completion = 0 }, -] +[verification-status] +current-checkout = "not compiler-verified" +idris2-builds = "not run; idris2 unavailable in the assessment workspace" +zig-builds-and-tests = "not run; zig unavailable in the assessment workspace" +just-recipes = "not executed; just unavailable in the assessment workspace" +static-smoke-checks = "source-pattern and inventory checks only; never formal proof or conformance evidence" +language-bindings = "operational support not established; see BINDINGS.a2ml" +container-deployment = "disabled; no deployable server binary or validated health endpoint is established" + +[scope-boundaries] +formal-claims = "An Idris2 build checks only the propositions in the package manifest; it does not prove independent Zig, C-header, or language-wrapper conformance." +network-services = "Directory names and state-machine models do not establish complete network servers." +cryptography = "DNSSEC, CA signing/validation, and PQC cryptographic operations are unavailable or fail closed where documented." [blockers-and-issues] issues = [ - "Idris2 not yet in CI — .idr files type-check locally but not in GitHub Actions", - "ABI-FFI-README.md still has template placeholders from rsr-template-repo", - "zig banned (2026-04-10) — nesy-solver-api connector is zig and must migrate to Zig over time", - "RGTV vault-broker not yet deployed to Fly.io (./deploy-fly.sh rgtv not run in production)", - "nesy-solver-api JSON output format violates no-JSON-emit rule (should be A2ML)", - "No Groove protocol wiring — no zig Groove client exists", + "Idris2 and Zig compiler-backed builds and runtime tests must be run against a named current revision.", + "Toolchain versions and the full package build matrix are not yet reproducibly established.", + "Generated ABI/header/symbol correspondence is not verified repository-wide.", + "Native linking and executable tests are not established for the language binding inventory.", + "Container and deployment artefacts are scaffolding only and are not a runnable server distribution.", ] [critical-next-actions] actions = [ - "Deploy RGTV: NESY_INGEST_TOKEN= ./deploy-fly.sh rgtv (from connectors/proven-nesy-solver-api/zig/)", - "Full stack deploy: ./deploy-fly.sh all", - "Continue ABI-FFI for remaining core primitives (Phase 2)", - "Add Idris2 type-checking to CI workflow (Phase 5)", - "Begin Gleam + Elixir bindings (Phase 4 — BEAM NIF/port infrastructure)", - "v1.1.0: proven-httpd + HAR integration + conative-gating consent layer", - "v1.2.0: Hypatia CI scan integration", + "Pin or explicitly document supported Idris2, Zig, Just, and binding toolchain versions.", + "Run the full Idris2 and Zig build/test loops with those toolchains; record exact versions, commit, commands, and results.", + "Add generated-header and ABI boundary/conformance checks for each bridge claimed as supported.", + "Revalidate every language binding by compiling, linking, and executing it against the intended native library; keep unavailable bindings fail-closed.", + "Keep container deployment disabled until a real executable server target, health endpoint, and image tests exist.", ] -[completed-work] -entries = [ - { date = "2026-04-11", description = "RGTV credential brokering: wired rgtv_client.v into nesy-solver-api, deploy-fly.sh rgtv phase added, RGTV_URL + RGTV_AGENT_TOKEN in fly.toml, vault-broker axum HTTP server built." }, - { date = "2026-04-11", description = "panic-attack unified-api-adapter CI: 16-surface protocol architecture in panic-attack.toml, GitHub Actions workflow panic-attack-unified-api-adapter.yml with per-surface attribution table, Justfile assail-unified-api-adapter recipe." }, - { date = "2026-04-11", description = "Fly.io cold-start hardening: echidna read_timeout 90s in server.v, deploy-fly.sh echidna grace_period 90s, echidna fly.toml HTTP health check + 90s grace, echidna Containerfile curl HEALTHCHECK with 90s start-period." }, - { date = "2026-04-11", description = "ROADMAP.adoc: v1.1.0 (HAR, conative-gating, invariant path), v1.2.0 (Hypatia CI), v1.3.0 (Lithoglyph aqueduct), v1.4.0 (cadre-tea-router + ReScript TEA) milestones added." }, - { date = "2026-04-11", description = "deploy-fly.sh: RGTV phase 0 added, dependency order updated (rgtv → clickhouse → schema → verisim → echidna → nesy-api), rgtv-agent-token state file management." }, - { date = "2026-04-11", description = "fly.toml .internal → .flycast fix: all internal service URLs use .flycast (auto-wake capable) not .internal." }, - { date = "2026-03-16", description = "7 protocols built or enhanced with ABI-FFI. 403 tests total across the server suite." }, - { date = "2026-03-01", description = "ABI-FFI for all 6 connectors: dbconn, authconn, cacheconn, queueconn, resolverconn, storageconn. 48 new files, ~5500 lines. All build and test with Zig 0.15.2." }, - { date = "2026-03-01", description = "6 connector interface skeletons added (Types.idr + Main.idr + top-level module)." }, - { date = "2026-01-30", description = "Initial commit: 102 protocol skeletons with dependent types." }, +[historical-evidence] +reports = [ + "audits/proof-panic-attack-2026-06-23.adoc", + "audits/audit-ffi-2026-05-26.adoc", ] +interpretation = "Historical reports apply only to the revisions and toolchains they record; they are not current verification." [maintenance-status] -last-run-utc = "2026-04-11T00:00:00Z" -last-report = "reports/maintenance/latest.json" -last-result = "unknown" -open-warnings = 0 -open-failures = 0 +last-assessment = "2026-09-27" +result = "current source checks reviewed; compiler verification blocked by missing toolchains" -[ecosystem] -part-of = ["proven ecosystem", "PanLL VAB"] -depends-on = ["idris2", "zig", "reasonably-good-token-vault", "echidna", "verisimdb"] +[recent-work] +entries = [ + { date = "2026-09-27", description = "Reconciled readiness and binding claims with inspected source; made unavailable authentication, DNSSEC, CA/PQC, and OCaml native operations fail closed; clarified source-smoke-test scope; repaired task definitions and current-status metadata. Compiler-backed builds remain unverified." }, +] diff --git a/.machine_readable/6a2/anchor/0-AI-MANIFEST.a2ml b/.machine_readable/6a2/anchor/0-AI-MANIFEST.a2ml index 0dd6825b..811d1f85 100644 --- a/.machine_readable/6a2/anchor/0-AI-MANIFEST.a2ml +++ b/.machine_readable/6a2/anchor/0-AI-MANIFEST.a2ml @@ -1,21 +1,8 @@ -# AI Manifest for Anchor Directory += Anchor Directory Guide -## Purpose - -This manifest declares the AI-assistant context for the anchor machine-readable metadata directory. - -## Canonical Locations - -ANCHOR.a2ml files MUST exist in this directory. - -## Multiple Versions - -Unlike other A2ML files, multiple versions of ANCHOR.a2ml with different dates MAY exist. -Each version represents a specific recalibration point. - -## Invariants - -- Multiple versions with different dates are permitted -- No other A2ML files in this directory -- Single source of truth for anchor documents +The repository's single canonical anchor is +`ANCHOR.a2ml` in this directory. +Do not create dated duplicate anchors here unless a documented migration +requires them. Keep the anchor's authority, language policy, golden path, and +semantic-file paths aligned with files that actually exist in the repository. diff --git a/.machine_readable/6a2/anchor/ANCHOR.a2ml b/.machine_readable/6a2/anchor/ANCHOR.a2ml index 56507453..4bfa9922 100644 --- a/.machine_readable/6a2/anchor/ANCHOR.a2ml +++ b/.machine_readable/6a2/anchor/ANCHOR.a2ml @@ -1,52 +1,55 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# ANCHOR.a2ml - authoritative anchor for this repository +# ANCHOR.a2ml — repository semantic and evidence boundary. [metadata] -version = "1.0.0" -last-updated = "2026-03-02" +version = "1.1.0" +last-updated = "2026-09-27" [anchor] schema = "hyperpolymath.anchor/1" repo = "hyperpolymath/proven-servers" -authority = "upstream-canonical" +authority = "repository-maintained" purpose = [ - "Define canonical semantics and policy boundaries for this repository.", - "Declare what downstream/satellite repos can extend but not redefine.", - "Provide a stable golden path and invariant contract for release readiness.", + "Define the source repository's purpose and verification boundary.", + "Prevent source presence, models, and historical audit reports from being mistaken for operational or current proof evidence.", + "Provide the repository-specific development and maintenance entry points.", ] [identity] project = "proven-servers" -kind = "library" # language | library | service | tool -one-sentence = "Dependently-typed protocol implementations with verified ABI/FFI" -domain = "network-protocols" +kind = "source-monorepo" +one-sentence = "A research and prototype monorepo containing protocol models, FFI experiments, language-binding sources, and supporting documentation." +domain = "protocol-models-and-ffi" [semantic-authority] -policy = "canonical" +policy = "canonical-within-this-repository" owns = [ - "Project semantics and specification", - "Invariant definitions and contractiles", - "Reference implementation behavior", + "Repository-specific project scope and evidence claims", + "Package-level model and FFI source where present", + "Repository maintenance policy and binding-readiness declarations", ] [implementation-policy] -allowed = ["Rust", "Idris2", "Zig", "Scheme", "Shell", "Just", "AsciiDoc", "Markdown"] -forbidden = ["Node.js", "npm"] +model-and-abi-languages = ["Idris2"] +native-ffi-language = ["Zig"] +existing-binding-source-languages = ["Ada", "C", "C++", "C#", "Dart", "Elixir", "Gleam", "Go", "Haskell", "Java", "JavaScript", "Julia", "Kotlin", "Lua", "OCaml", "PHP", "Python", "ReScript", "Ruby", "Rust", "Swift"] +new-language-policy = "Require a documented scope and toolchain rationale; existing binding directories do not establish support." +license = "MPL-2.0 for original source, subject to file-level SPDX and third-party license notices." [golden-path] -smoke-test-command = [ - "just test", - "just quality", -] +source-smoke-test = "just test-static" +compiler-backed-test = "just test" +quality-command = "just quality" +limitations = "Compiler-backed tasks require Just, Idris2, and Zig; static smoke tasks are not proofs or conformance tests." success-criteria = [ - "Core tests pass", - "Quality gates pass", - "No unresolved critical security findings", + "Relevant package builds and executable tests pass on the exact revision and recorded toolchains.", + "Model, ABI, generated header, native implementation, and binding correspondence is tested for each claimed bridge.", + "Readiness and deployment claims are supported by current evidence.", ] [satellite-policy] @@ -56,7 +59,9 @@ must-have-anchor = true must-have-golden-path = true [semantic-authority-files] -language-spec = "SPECIFICATION.md" -formal-proofs = "docs/proofs/PROOFS.adoc" -type-theory = "docs/theory/THEORY.adoc" -algorithms = "docs/theory/ALGORITHMS.adoc" +human-overview = "README.adoc" +current-state = "READINESS.adoc" +verification-needs = "PROOF-NEEDS.adoc" +architecture-metadata = ".machine_readable/6a2/META.a2ml" +project-state = ".machine_readable/6a2/STATE.a2ml" +task-entrypoint = "Justfile" diff --git a/.machine_readable/BINDINGS.a2ml b/.machine_readable/BINDINGS.a2ml index f03a9191..ed7d790c 100644 --- a/.machine_readable/BINDINGS.a2ml +++ b/.machine_readable/BINDINGS.a2ml @@ -1,25 +1,19 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) Jonathan D.A. Jewell # -# Binding registry & policy for proven-servers. -# Single source of truth for tools/check-binding-policy.sh (the CI tripwire) -# and for the "20 language bindings" claim in README / EXPLAINME / READINESS. -# -# Policy: a binding is a THIN wrapper over the generated C ABI — it either calls -# the ABI via that language's FFI, or declares ABI-conformant constants/types — -# and carries NO reimplemented safety logic (validation, parsing, state -# machines). See docs/decisions/0003-keep-bindings-thin-abi-wrappers.md +# Binding directory inventory and conservative readiness declaration. +# Directory presence does not establish that a binding builds, links, or is supported. [policy] -model = "thin-abi-wrapper" -no-logic = true -abi-source = "generated/abi — per-protocol C headers; shared libs libproven_" -reference-bindings = ["ada", "java"] -decision = "docs/decisions/0003-keep-bindings-thin-abi-wrappers.md" +model = "mixed language-specific source wrappers and scaffolds" +no-protocol-reimplementation = true +abi-source = "Per-protocol exported C ABI; availability and coverage vary by package" +decision = "docs/decisions/0003-keep-bindings-thin-abi-wrappers.adoc" +readiness-rule = "A source wrapper is not considered wired until its native bridge links and its tests pass." [registry] -# Directory names under bindings/ — the authoritative set. The tripwire asserts -# that `bindings/*` on disk equals this list exactly (no unregistered binding). +# Number of language-named directories under bindings/; this is inventory only, +# not a claim of 20 supported or fully functional language bindings. count = 20 languages = [ "ada", "cpp", "csharp", "dart", "elixir", "gleam", "go", "haskell", @@ -27,37 +21,38 @@ languages = [ "python", "rescript", "ruby", "rust", "swift", ] -# Per-binding tier: -# wired = calls the C ABI via FFI; no reimplemented logic -# scaffold = constants/types only; FFI wiring pending; NO logic permitted +# Source-level status only. Native build/link/test coverage has not been +# re-established for every binding in the current checkout. [tier] -ada = { status = "wired", ffi = "pragma Import (C)", note = "reference binding" } -java = { status = "wired", ffi = "JNI (System.loadLibrary)", note = "reference binding" } -rust = { status = "wired", ffi = "extern \"C\"" } -go = { status = "wired", ffi = "cgo" } -python = { status = "wired", ffi = "ctypes" } -ocaml = { status = "wired", ffi = "external (C stubs)" } -julia = { status = "wired", ffi = "ccall" } -cpp = { status = "wired", ffi = "extern \"C\"" } -csharp = { status = "wired", ffi = "DllImport (P/Invoke)" } -dart = { status = "wired", ffi = "dart:ffi" } -haskell = { status = "wired", ffi = "foreign import" } -kotlin = { status = "wired", ffi = "external (JNI)" } -lua = { status = "wired", ffi = "luajit ffi" } -php = { status = "wired", ffi = "FFI" } -ruby = { status = "wired", ffi = "Fiddle/FFI" } -swift = { status = "wired", ffi = "@_silgen_name" } -elixir = { status = "scaffold", ffi = "none", note = "unproven logic removed 2026-06-24; NIF wiring pending — ADR 0003" } -gleam = { status = "scaffold", ffi = "none", note = "unproven logic removed 2026-06-24; @external wiring pending — ADR 0003" } -rescript = { status = "scaffold", ffi = "partial", note = "unproven logic removed 2026-06-24; @module wiring partial — ADR 0003" } +ada = { status = "source-wrapper", ffi = "pragma Import (C)" } +cpp = { status = "source-wrapper", ffi = "extern C declarations" } +csharp = { status = "source-wrapper", ffi = "DllImport (P/Invoke)" } +dart = { status = "source-wrapper", ffi = "dart:ffi" } +go = { status = "source-wrapper", ffi = "cgo" } +haskell = { status = "source-wrapper", ffi = "foreign import" } +javascript = { status = "source-wrapper", ffi = "package-specific; not uniformly verified" } +julia = { status = "source-wrapper", ffi = "ccall" } +lua = { status = "source-wrapper", ffi = "LuaJIT FFI" } +php = { status = "source-wrapper", ffi = "FFI" } +python = { status = "source-wrapper", ffi = "ctypes" } +ruby = { status = "source-wrapper", ffi = "Fiddle/FFI" } +rust = { status = "source-wrapper", ffi = "extern C declarations" } +swift = { status = "source-wrapper", ffi = "symbol declarations; ABI compatibility unverified" } +java = { status = "native-declarations", ffi = "JNI native methods; JNI implementation not present here" } +kotlin = { status = "native-declarations", ffi = "JNI native methods; JNI implementation not present here" } +ocaml = { status = "unavailable", ffi = "fail-closed OCaml functions; compatible C stubs are not implemented" } +elixir = { status = "scaffold", ffi = "none", note = "FFI/NIF wiring pending" } +gleam = { status = "scaffold", ffi = "none", note = "FFI/port wiring pending" } +rescript = { status = "scaffold", ffi = "partial", note = "binding scaffold; generated native module and tests are not validated" } -[audit] -# Depth of the 2026-06-24 logic audit. Deep audit = manually verified -# logic-free / logic-removed. FFI-linked = confirmed to call libproven_* but -# not line-by-line audited for stray logic in that pass. -deep-audited = ["ada", "java", "elixir", "gleam", "rescript"] -ffi-linked-not-deep-audited = [ +[historical-audit] +# The entries below preserve the scope of a prior 2026-06-24 source audit. +# They are not current build/link evidence, and the OCaml status above has since +# been corrected after finding raw C symbols declared as OCaml primitives. +date = "2026-06-24" +status = "historical snapshot; revalidation required" +previously-reviewed = ["ada", "java", "elixir", "gleam", "rescript"] +previously-listed-as-ffi-linked = [ "rust", "go", "python", "ocaml", "julia", "cpp", "csharp", "dart", "haskell", "kotlin", "lua", "php", "ruby", "swift", ] -date = "2026-06-24" diff --git a/.machine_readable/ai/README.adoc b/.machine_readable/ai/README.adoc index 7d90fea3..b333f179 100644 --- a/.machine_readable/ai/README.adoc +++ b/.machine_readable/ai/README.adoc @@ -1,24 +1,20 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell +// Copyright (c) 2026 Jonathan D.A. Jewell = AI Guidance Directory -Put AI-facing instructions in this folder. +This directory is reserved for longer AI-facing guidance. Tool-specific root +instruction files are summaries that point to `docs/AI-CONVENTIONS.adoc`. -Examples: +== Canonical Read Order -* `CLAUDE.md` -* `COPILOT.md` -* `GEMINI.md` -* `AI.a2ml` -* `AI.djot` +. `0-AI-MANIFEST.a2ml` +. `.machine_readable/6a2/STATE.a2ml` +. `.machine_readable/6a2/anchor/ANCHOR.a2ml` +. `.machine_readable/policies/MAINTENANCE-AXES.a2ml` +. The package-local README and manifest relevant to the change -Avoid scattering agent instruction files around the repo root. +== Evidence Boundary -Recommended machine read order: - -* `.machine_readable/anchors/ANCHOR.a2ml` -* `.machine_readable/policies/MAINTENANCE-AXES.a2ml` -* `.machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml` -* `.machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml` -* `.machine_readable/STATE.a2ml` -* `.machine_readable/META.a2ml` +Do not infer build, support, deployment, formal proof, or conformance from +source presence. Report compiler/tool unavailability and static-only checks +explicitly. The operational guidance is in `docs/AI-CONVENTIONS.adoc`. diff --git a/.machine_readable/bot_directives/README.adoc b/.machine_readable/bot_directives/README.adoc index 1dcb6257..174fc9c5 100644 --- a/.machine_readable/bot_directives/README.adoc +++ b/.machine_readable/bot_directives/README.adoc @@ -1,41 +1,30 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Agent Instructions -:toc: preamble +// Copyright (c) 2026 Jonathan D.A. Jewell += Bot Directives +:toc: -Methodology-aware configuration for AI agents. Read by any AI agent -(Claude, Gemini, Copilot, etc.) at session start. +This directory holds repository-maintained bot coverage, debt, and methodology +metadata. It does not configure or prove that an external bot is running. == Files [cols="1,3"] |=== -| File | Purpose +|File |Purpose -| `methodology.a2ml` -| Default mode, invariants, ring ceiling, priority weights, convergent budget +|`methodology.a2ml` +|Maintenance sequence and evidence expectations -| `coverage.a2ml` -| Session coverage tracking — what was visited, what was skipped, what has MUSTs +|`coverage.a2ml` +|Declared inspection coverage and explicitly skipped areas -| `debt.a2ml` -| Meander debt — things found but not fixed, carried between sessions +|`debt.a2ml` +|Known outstanding work carried between maintenance passes |=== -== How Agents Use These +== Agent Guidance -1. Read `methodology.a2ml` at session start — know mode, invariants, ceiling -2. Read `coverage.a2ml` — know what was visited last time, what was skipped -3. Read `debt.a2ml` — know what's outstanding from previous sessions -4. At session end, update `coverage.a2ml` and `debt.a2ml` - -== Relationship to Other Files - -* `AGENTIC.a2ml` says WHAT agents can do (permissions, gating) -* `bot_directives/` says HOW agents should work (methodology) -* `bot_directives/` says what the gitbot-fleet does (fleet-specific) -* `CLAUDE.md` says how Claude specifically should work (Claude-specific) - -== Reference - -ADR-002 in `standards/agentic-a2ml/docs/ADR-002-methodology-layer.adoc` +Read `.machine_readable/6a2/AGENTIC.a2ml` for safety and evidence boundaries, +and `docs/AI-CONVENTIONS.adoc` for human-readable instructions. Tool-specific +configuration files are summaries only; none of these files imply that a +particular hosted bot or external service is active. diff --git a/.machine_readable/contractiles/Adjustfile.a2ml b/.machine_readable/contractiles/Adjustfile.a2ml index 6f01e89f..dd27bdef 100644 --- a/.machine_readable/contractiles/Adjustfile.a2ml +++ b/.machine_readable/contractiles/Adjustfile.a2ml @@ -1,72 +1,31 @@ # SPDX-License-Identifier: MPL-2.0 -# Adjustfile — Drift-tolerance contract for rsr-template-repo -# Author: Jonathan D.A. Jewell -# -# Cumulative-drift catchment: tolerance bands + corrective actions. -# Authority: advisory (Yard) — continue-with-warnings; auto_fix where deterministic. -# Run with: adjust check -# Fix with: adjust fix (applies deterministic patches; advisory otherwise) +# Adjustfile — repository drift checks and bounded follow-up. @abstract: -Drift tolerances and corrective actions for rsr-template-repo. Unlike -MUST (hard gate), ADJUST tracks cumulative drift against tolerance bands -and proposes corrective actions. Advisory — it warns and trends, it does -not block. +Advisory checks for drift between current source, machine metadata, human +status, and configured tasks. A warning requires review; it is not a build or +release gate unless an executable check is explicitly wired. @end -## Template Drift +[metadata] +version = "1.1.0" +last-updated = "2026-09-27" -### placeholder-drift -- description: Template placeholders should be replaced when copied -- tolerance: 0 placeholder markers in copied repos -- corrective: Search and replace all {{PLACEHOLDER}} markers -- severity: advisory -- notes: This check only applies to repos that copied from this template +[documentation-drift] +readiness-parity = "Keep READINESS.adoc and PROOF-NEEDS.adoc aligned with current tool and test evidence." +package-claim-accuracy = "Do not generalize package-specific or historical results to the whole repository." +path-reference-accuracy = "Use current .machine_readable/6a2, .adoc, SECURITY.adoc, and root Justfile paths." -### template-version-drift -- description: Template version should match RSR spec version -- tolerance: Template version matches current RSR spec -- corrective: Update template to match latest RSR spec -- severity: advisory +[structural-drift] +root-task-copy = "Keep .machine_readable/contractiles/Justfile byte-identical to root Justfile." +no-duplicate-core-metadata = "Keep the six core A2ML files under .machine_readable/6a2 only." +contractile-tasks = "Do not add destructive rollback or template checks for files this repository does not contain." -## Documentation Drift +[capability-drift] +binding-inventory = "Update BINDINGS.a2ml only after current native build/link/runtime evidence." +container-status = "Keep deployment disabled until a real executable and runtime checks exist." +toolchain-evidence = "Record exact supported versions before describing builds as reproducible." -### readme-completeness -- description: README should document all template features -- tolerance: README covers all contractiles and directory structure -- corrective: Update README.adoc with missing sections -- severity: advisory - -### example-accuracy -- description: Examples in documentation should match actual template content -- tolerance: All code examples in docs are accurate -- corrective: Audit and fix examples in documentation -- severity: advisory - -## Structural Drift - -### contractile-sync -- description: All contractiles should have matching a2ml and ncl implementations -- tolerance: Every .a2ml has a corresponding .ncl -- corrective: Generate missing .ncl files from .a2ml -- severity: advisory - -### no-broken-symlinks -- description: No broken symbolic links in template structure -- tolerance: 0 broken symlinks -- corrective: Run symlink-check script -- severity: advisory - -## Accessibility Drift - -### adoc-not-md -- description: Template docs should prefer AsciiDoc -- tolerance: New prose docs are *.adoc -- corrective: Convert any new *.md to *.adoc -- severity: advisory - -### spdx-header-consistency -- description: All template files have correct SPDX headers -- tolerance: 0 files missing SPDX-License-Identifier -- corrective: Add SPDX headers to files that need them -- severity: advisory +[review] +automatic-fixes = false +status = "advisory; review with just validate and package-specific checks" diff --git a/.machine_readable/contractiles/Intentfile.a2ml b/.machine_readable/contractiles/Intentfile.a2ml index ef74f452..be7e68e2 100644 --- a/.machine_readable/contractiles/Intentfile.a2ml +++ b/.machine_readable/contractiles/Intentfile.a2ml @@ -1,99 +1,44 @@ # SPDX-License-Identifier: MPL-2.0 -# Intentfile (A2ML Canonical) — north-star contractile for rsr-template-repo -# Author: Jonathan D.A. Jewell -# -# Paired runner: intend.ncl -# Verb: intend -# -# Semantics: North-star contractile. Declares BOTH concrete committed -# next-actions AND horizon aspirations the project wishes to -# become. Two sections share one file because they answer -# the same question at different ranges: -# [[intents]] — "we WILL do this; track progress" -# status: declared → in_progress → done | -# deferred | retired -# [[wishes]] — "we WISH this were true; revisit later" -# status: declared → in_progress → achieved | -# abandoned -# grouped by horizon: near / mid / far. -# Non-gating — this is a report, not a gate. See the `must` -# contractile for hard gates. +# Intentfile — declared work and longer-term goals for proven-servers. @abstract: -North-star contractile for rsr-template-repo. This repository is the -canonical template for Rhodium Standard Repository compliance. It provides -the scaffold that all hyperpolymath repos should copy and customize. +This file records current repository intent. It is not evidence that an +implementation or check has already been completed. @end -## Purpose - -The rsr-template-repo serves as the master template for all hyperpolymath -repositories. It contains the complete set of contractile files, machine-readable -specifications, and governance documentation that define the Rhodium Standard. - -Every new repository in the hyperpolymath estate should be initialized by -copying this template and substituting the placeholder values with -repo-specific content. - -## Anti-Purpose - -This repository is NOT: -- A general-purpose project scaffold for external use (hyperpolymath-only) -- A replacement for per-repo customization (all files must be bespoke) -- A static template that never changes (evolves with RSR spec) -- A runtime library or framework (build-time only) - -## If In Doubt - -If you are unsure whether a change is in scope, ask. Sensitive areas: -- .machine_readable/ contractile definitions -- RSR specification files -- Governance templates -- License policy documents - -## Committed Next-Actions - -### repo-initialization -- description: Provide just copy-and-substitute template for new repos -- probe: test -f scripts/init-repo.sh -- status: done -- notes: Run with source scripts/init-repo.sh - -### contractile-completeness -- description: Every RSR contractile has an a2ml and ncl implementation -- probe: ls .machine_readable/contractiles/*.a2ml | wc -l | grep -q "^6$" -- status: in_progress -- notes: Currently 6 contractile verbs: intend, must, trust, adjust, bust, dust - -### automation-scripts -- description: All repetitive tasks have just recipes -- probe: grep -c "^# " Justfile | grep -q "^[6-9][0-9]*$" -- status: in_progress - -## Wishes - -### Near Horizon - -#### cross-repo-validation -- description: Tooling to validate all repos against RSR spec -- horizon: near -- status: declared - -#### automated-substitution -- description: Script to automate repo-specific substitution in template -- horizon: near -- status: declared - -### Mid Horizon - -#### formal-verification -- description: Idris2 proofs for all critical contractile invariants -- horizon: mid -- status: declared - -### Far Horizon - -#### ecosystem-visualization -- description: Interactive graph of all hyperpolymath repos and dependencies -- horizon: far -- status: declared +[intents] + +[[item]] +id = "reproducible-toolchains" +description = "Record supported Idris2, Zig, Just, and binding toolchain versions." +status = "in-progress" +priority = "must" + +[[item]] +id = "compiler-backed-package-matrix" +description = "Run all maintained Idris2 manifests and Zig build/test targets on a named revision; record failures and versions." +status = "blocked-on-toolchains" +priority = "must" + +[[item]] +id = "abi-ffi-correspondence" +description = "Add generated-header, ABI-layout, symbol-signature, and native boundary checks for each bridge claimed as supported." +status = "declared" +priority = "must" + +[[item]] +id = "binding-readiness" +description = "Compile, link, and execute each binding before upgrading its inventory status." +status = "in-progress" +priority = "intend" + +[[item]] +id = "container-deployment" +description = "Keep container builds and publishing disabled until a real service executable and runtime tests exist." +status = "blocked-on-application-target" +priority = "must" + +[aspirations] +formatter = "Adopt and pin language-specific formatters after the supported language/package scope is decided." +ci-matrix = "Run the supported compiler/test matrix in CI and preserve per-package results." +protocol-vectors = "Add authoritative protocol vectors, malformed-input suites, and interoperability evidence where applicable." diff --git a/.machine_readable/contractiles/Justfile b/.machine_readable/contractiles/Justfile index e9f49f1e..fc47171a 100644 --- a/.machine_readable/contractiles/Justfile +++ b/.machine_readable/contractiles/Justfile @@ -1,970 +1,395 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# RSR Standard Justfile Template -# https://just.systems/man/en/ -# -# Copy this file to new projects and customize the placeholder values. -# -# Run `just` to see all available recipes -# Run `just cookbook` to generate docs/just-cookbook.adoc -# Run `just combinations` to see matrix recipe options +# Repository tasks for proven-servers. Build/test commands are package-scoped +# and fail when required compilers are unavailable. Static smoke checks are +# labelled separately from compiler-backed verification. set shell := ["bash", "-uc"] set dotenv-load := true set positional-arguments := true -# Import auto-generated contractile recipes +# Optional contractile checks; the file contains only repository-specific, +# non-destructive recipes. import? "contractile.just" -# Project metadata — customize these project := "proven-servers" -version := "0.1.0" -tier := "infrastructure" # 1 | 2 | infrastructure - -# ═══════════════════════════════════════════════════════════════════════════════ -# DEFAULT & HELP -# ═══════════════════════════════════════════════════════════════════════════════ -# Show all available recipes with descriptions +# Show the available, configured repository tasks. default: @just --list --unsorted -# Show detailed help for a specific recipe help recipe="": #!/usr/bin/env bash + set -euo pipefail if [ -z "{{recipe}}" ]; then just --list --unsorted - echo "" - echo "Usage: just help " - echo " just cookbook # Generate full documentation" - echo " just combinations # Show matrix recipes" else - just --show "{{recipe}}" 2>/dev/null || echo "Recipe '{{recipe}}' not found" + just --show "{{recipe}}" fi -# Show this project's info info: - @echo "Project: {{project}}" - @echo "Version: {{version}}" - @echo "RSR Tier: {{tier}}" - @echo "Recipes: $(just --summary | wc -w)" - @[ -f ".machine_readable/STATE.a2ml" ] && grep -oP 'phase\s*=\s*"\K[^"]+' .machine_readable/STATE.a2ml | head -1 | xargs -I{} echo "Phase: {}" || true - -# ═══════════════════════════════════════════════════════════════════════════════ -# INIT — Bootstrap a new project from this template -# ═══════════════════════════════════════════════════════════════════════════════ - -# Interactive project bootstrap — replaces all {{PLACEHOLDER}} tokens -init: #!/usr/bin/env bash set -euo pipefail + echo "Project: {{project}}" + echo "Current phase: $(sed -n 's/^phase = \"\(.*\)\"/\1/p' .machine_readable/6a2/STATE.a2ml | head -1)" + echo "Readiness: $(sed -n 's/^\*\*Current Grade:\*\* \([A-FX]\).*/\1/p' READINESS.adoc | head -1)" + echo "Toolchains: Idris2 and Zig are required for compiler-backed package checks." + +# Build every Idris2 package manifest under protocols/, core/, and connectors/. +# The root module tree and not-proven/ examples are not package manifests in +# this configured build set. +build-idris: + #!/usr/bin/env bash + set -euo pipefail + command -v idris2 >/dev/null 2>&1 || { echo "ERROR: idris2 is required" >&2; exit 127; } + count=0 + while IFS= read -r -d '' package; do + package_dir="$(dirname "$package")" + package_name="$(basename "$package")" + printf '==> idris2 --build %s/%s\n' "$package_dir" "$package_name" + (cd "$package_dir" && idris2 --build "$package_name") + count=$((count + 1)) + done < <(find protocols core connectors -type f -name '*.ipkg' -print0 | sort -z) + [ "$count" -gt 0 ] || { echo "ERROR: no Idris2 package manifests found" >&2; exit 1; } + echo "Built $count Idris2 package manifests." + +# Build the root FFI example and every Zig build manifest under the maintained +# protocol/core/connector trees. not-proven/ is intentionally excluded. +build-zig: + #!/usr/bin/env bash + set -euo pipefail + command -v zig >/dev/null 2>&1 || { echo "ERROR: zig is required" >&2; exit 127; } + count=0 + while IFS= read -r -d '' build_file; do + build_dir="$(dirname "$build_file")" + printf '==> zig build (%s)\n' "$build_dir" + (cd "$build_dir" && zig build) + count=$((count + 1)) + done < <({ find protocols core connectors -type f -name 'build.zig' -print0; printf 'ffi/zig/build.zig\0'; } | sort -z) + [ "$count" -gt 0 ] || { echo "ERROR: no Zig build manifests found" >&2; exit 1; } + echo "Built $count Zig packages/examples." + +# Build all configured Idris2 and Zig packages. Both compilers are required. +build: build-idris build-zig + +# There is no uniform release profile across package-specific builds. +build-release: + @echo "ERROR: no repository-wide release profile is configured; use the package's documented Zig options." >&2 + @exit 2 + +# There is no repository-wide incremental/watch build target. +build-watch: + @echo "ERROR: no repository-wide build-watch target is configured." >&2 + @exit 2 - echo "═══════════════════════════════════════════════════" - echo " RSR Project Bootstrap" - echo "═══════════════════════════════════════════════════" - echo "" - - # --- Load defaults from config (if exists) --- - # Create yours: ~/.config/rsr/defaults - # Format: OWNER=myorg AUTHOR="My Name" AUTHOR_EMAIL=me@example.org ... - DEFAULTS="${XDG_CONFIG_HOME:-$HOME/.config}/rsr/defaults" - if [ -f "$DEFAULTS" ]; then - echo "Loading defaults from $DEFAULTS" - # shellcheck source=/dev/null - source "$DEFAULTS" - echo "" - fi - - # --- Required values (pre-filled from defaults if available) --- - read -rp "Project name (human-readable, e.g. My Project): " PROJECT_NAME - [ -z "$PROJECT_NAME" ] && echo "Error: project name required" && exit 1 +# Remove compiler outputs only from maintained source/package trees. +clean: + #!/usr/bin/env bash + set -euo pipefail + find protocols core connectors bindings ffi -type d \( -name build -o -name zig-out -o -name _build \) -prune -exec rm -rf {} + + rm -rf target/ _build/ dist/ out/ obj/ bin/ - read -rp "Repository slug (e.g. my-project): " REPO - [ -z "$REPO" ] && echo "Error: repo slug required" && exit 1 +clean-all: clean + rm -rf .cache .tmp - read -rp "Owner [${OWNER:-}]: " _OWNER - OWNER="${_OWNER:-${OWNER:-}}" - [ -z "$OWNER" ] && echo "Error: owner required" && exit 1 +# Run Zig tests for every discovered build manifest and the root FFI example. +test-zig: + #!/usr/bin/env bash + set -euo pipefail + command -v zig >/dev/null 2>&1 || { echo "ERROR: zig is required" >&2; exit 127; } + count=0 + while IFS= read -r -d '' build_file; do + test_dir="$(dirname "$build_file")" + printf '==> zig build test (%s)\n' "$test_dir" + (cd "$test_dir" && zig build test) + count=$((count + 1)) + done < <({ find protocols core connectors -type f -name 'build.zig' -print0; printf 'ffi/zig/build.zig\0'; } | sort -z) + [ "$count" -gt 0 ] || { echo "ERROR: no Zig test targets found" >&2; exit 1; } + echo "Ran Zig test targets for $count packages/examples." - read -rp "Author full name [${AUTHOR:-}]: " _AUTHOR - AUTHOR="${_AUTHOR:-${AUTHOR:-}}" - [ -z "$AUTHOR" ] && echo "Error: author name required" && exit 1 +# Source-pattern heuristics, selected policy checks, and language inventory; +# these are not substitutes for compiler tests or formal proofs. +test-static: + bash tests/source_smoke_test.sh + bash tests/aspect/security_test.sh + bash tests/binding_inventory.sh - read -rp "Author email [${AUTHOR_EMAIL:-}]: " _AUTHOR_EMAIL - AUTHOR_EMAIL="${_AUTHOR_EMAIL:-${AUTHOR_EMAIL:-}}" - [ -z "$AUTHOR_EMAIL" ] && echo "Error: email required" && exit 1 +test: build-idris test-zig test-static - # --- Optional values (pre-filled from defaults if available) --- - read -rp "Author organization [${AUTHOR_ORG:-none}]: " _AUTHOR_ORG - AUTHOR_ORG="${_AUTHOR_ORG:-${AUTHOR_ORG:-}}" +test-verbose: test - read -rp "Previous/alt email [${AUTHOR_EMAIL_ALT:-none}]: " _AUTHOR_EMAIL_ALT - AUTHOR_EMAIL_ALT="${_AUTHOR_EMAIL_ALT:-${AUTHOR_EMAIL_ALT:-}}" +test-smoke: test-static - read -rp "Project description []: " PROJECT_DESCRIPTION +source-smoke: + bash tests/source_smoke_test.sh - read -rp "Forge domain [${FORGE:-github.com}]: " _FORGE - FORGE="${_FORGE:-${FORGE:-github.com}}" +security-test: + bash tests/aspect/security_test.sh - read -rp "Security contact email [${SECURITY_EMAIL:-$AUTHOR_EMAIL}]: " _SECURITY_EMAIL - SECURITY_EMAIL="${_SECURITY_EMAIL:-${SECURITY_EMAIL:-$AUTHOR_EMAIL}}" +binding-inventory: + bash tests/binding_inventory.sh - read -rp "Conduct contact email [${CONDUCT_EMAIL:-$AUTHOR_EMAIL}]: " _CONDUCT_EMAIL - CONDUCT_EMAIL="${_CONDUCT_EMAIL:-${CONDUCT_EMAIL:-$AUTHOR_EMAIL}}" +e2e: + bash tests/e2e.sh - read -rp "Project type (library|binary|monorepo|service|website) [library]: " PROJECT_TYPE - PROJECT_TYPE="${PROJECT_TYPE:-library}" +# Run configured checks only; language-specific formatting is not configured. +quality: fmt-check lint test + @echo "Configured build, test, shell, and policy checks passed." - read -rp "Website URL [https://${FORGE}/${OWNER}/${REPO}]: " WEBSITE - WEBSITE="${WEBSITE:-https://${FORGE}/${OWNER}/${REPO}}" +# No repository-wide formatter is declared. Do not report a no-op as formatting. +fmt: + @echo "ERROR: no repository-wide formatter is configured." >&2 + @exit 2 - # --- Container values (optional — only relevant if container/ exists) --- - if [ -d "container" ]; then - echo "" - echo "── Container configuration (optional) ─────────" - read -rp "Service name [${REPO}]: " _SERVICE_NAME - SERVICE_NAME="${_SERVICE_NAME:-${REPO}}" - read -rp "Primary port [8080]: " _PORT - PORT="${_PORT:-8080}" - read -rp "Container registry [ghcr.io/${OWNER}]: " _REGISTRY - REGISTRY="${_REGISTRY:-ghcr.io/${OWNER}}" - else - SERVICE_NAME="${REPO}" - PORT="8080" - REGISTRY="ghcr.io/${OWNER}" - fi +# Check whitespace in staged and unstaged changes; not a language formatter. +fmt-check: + git diff --check + git diff --cached --check - # --- Derived values --- - PROJECT_UPPER=$(echo "$REPO" | tr '[:lower:]-' '[:upper:]_') - PROJECT_LOWER=$(echo "$REPO" | tr '[:upper:]-' '[:lower:]_') - CURRENT_YEAR=$(date +%Y) - CURRENT_DATE=$(date +%Y-%m-%d) - VERSION="0.1.0" - - # Derive citation name parts (best-effort split on last space) - AUTHOR_LAST="${AUTHOR##* }" - AUTHOR_FIRST="${AUTHOR% *}" - FIRST_INITIAL="${AUTHOR_FIRST:0:1}." - if [ "$AUTHOR_LAST" = "$AUTHOR_FIRST" ]; then - AUTHOR_FIRST="$AUTHOR" - AUTHOR_LAST="" - FIRST_INITIAL="" - fi +# Validate shell syntax and the binding inventory/scaffold policy. +lint: + #!/usr/bin/env bash + set -euo pipefail + while IFS= read -r -d '' script; do + bash -n "$script" + done < <(find . -path './.git' -prune -o -type f -name '*.sh' -print0) + bash tools/check-binding-policy.sh + echo "Shell syntax and binding-policy checks passed; no complete multi-language lint matrix is configured." - echo "" - echo "── Summary ──────────────────────────────────────" - echo " Project: $PROJECT_NAME" - echo " Repo: $REPO" - echo " Owner: $OWNER" - echo " Author: $AUTHOR <$AUTHOR_EMAIL>" - [ -n "$AUTHOR_ORG" ] && echo " Organization: $AUTHOR_ORG" - echo " Forge: $FORGE" - echo " Year: $CURRENT_YEAR" - echo "────────────────────────────────────────────────" - echo "" - read -rp "Proceed? [Y/n] " CONFIRM - [[ "${CONFIRM:-Y}" =~ ^[Nn] ]] && echo "Aborted." && exit 0 - - echo "" - echo "Replacing placeholders..." - - # Brace tokens as variables (hex avoids just interpolation) - LB=$(printf '\x7b\x7b') - RB=$(printf '\x7d\x7d') - - # Build the sed expression list - # Note: using | as delimiter since URLs contain / - SED_ARGS=( - -e "s|${LB}PROJECT_NAME${RB}|${PROJECT_NAME}|g" - -e "s|${LB}PROJECT_DESCRIPTION${RB}|${PROJECT_DESCRIPTION}|g" - -e "s|${LB}PROJECT${RB}|${PROJECT_UPPER}|g" - -e "s|${LB}project${RB}|${PROJECT_LOWER}|g" - -e "s|${LB}REPO${RB}|${REPO}|g" - -e "s|${LB}OWNER${RB}|${OWNER}|g" - -e "s|${LB}AUTHOR${RB}|${AUTHOR}|g" - -e "s|${LB}AUTHOR_EMAIL${RB}|${AUTHOR_EMAIL}|g" - -e "s|${LB}AUTHOR_ORG${RB}|${AUTHOR_ORG}|g" - -e "s|${LB}AUTHOR_LAST${RB}|${AUTHOR_LAST}|g" - -e "s|${LB}AUTHOR_FIRST${RB}|${AUTHOR_FIRST}|g" - -e "s|${LB}AUTHOR_INITIALS${RB}|${FIRST_INITIAL}|g" - -e "s|${LB}FORGE${RB}|${FORGE}|g" - -e "s|${LB}CURRENT_YEAR${RB}|${CURRENT_YEAR}|g" - -e "s|${LB}CURRENT_DATE${RB}|${CURRENT_DATE}|g" - -e "s|${LB}DATE${RB}|${CURRENT_DATE}|g" - -e "s|${LB}SECURITY_EMAIL${RB}|${SECURITY_EMAIL}|g" - -e "s|${LB}CONDUCT_EMAIL${RB}|${CONDUCT_EMAIL}|g" - -e "s|${LB}LICENSE${RB}|MPL-2.0|g" - -e "s|${LB}CONDUCT_TEAM${RB}|Code of Conduct Committee|g" - -e "s|${LB}RESPONSE_TIME${RB}|48 hours|g" - -e "s|${LB}MAIN_BRANCH${RB}|main|g" - -e "s|${LB}PROJECT_PURPOSE${RB}|${PROJECT_DESCRIPTION}|g" - -e "s|${LB}PROJECT_ROLE${RB}|${PROJECT_TYPE}|g" - -e "s|${LB}PROJECT_TYPE${RB}|${PROJECT_TYPE}|g" - -e "s|${LB}WEBSITE${RB}|${WEBSITE}|g" - -e "s|${LB}SERVICE_NAME${RB}|${SERVICE_NAME}|g" - -e "s|${LB}PORT${RB}|${PORT}|g" - -e "s|${LB}REGISTRY${RB}|${REGISTRY}|g" - -e "s|${LB}IMAGE${RB}|${REGISTRY}/${SERVICE_NAME}|g" - -e "s|${LB}VERSION${RB}|${VERSION}|g" - -e "s|${LB}EMAIL${RB}|${AUTHOR_EMAIL}|g" - ) - [ -n "$AUTHOR_EMAIL_ALT" ] && SED_ARGS+=(-e "s|${LB}AUTHOR_EMAIL_ALT${RB}|${AUTHOR_EMAIL_ALT}|g") - - # Replace in all text files (skip .git, LICENSE text, and binaries) - find . -type f \ - -not -path './.git/*' \ - -not -name 'MPL-2.0.txt' \ - -not -name '*.png' -not -name '*.jpg' -not -name '*.gif' \ - -not -name '*.woff' -not -name '*.woff2' \ - | while read -r file; do - if file --brief "$file" | grep -qi 'text\|ascii\|utf'; then - sed -i "${SED_ARGS[@]}" "$file" +# Report required compiler/task-runner availability; missing tools fail the task. +deps: + #!/usr/bin/env bash + set -euo pipefail + missing=0 + for tool in just idris2 zig; do + if command -v "$tool" >/dev/null 2>&1; then + printf 'available: %s (%s)\n' "$tool" "$(command -v "$tool")" + else + printf 'missing: %s\n' "$tool" >&2 + missing=1 fi done + [ "$missing" -eq 0 ] || exit 127 - # Also replace [YOUR-REPO-NAME] and [YOUR-NAME/ORG] in AI manifest - sed -i "s|\[YOUR-REPO-NAME\]|${PROJECT_NAME}|g" 0-AI-MANIFEST.a2ml 2>/dev/null || true - sed -i "s|\[YOUR-NAME/ORG\]|${OWNER}|g" 0-AI-MANIFEST.a2ml 2>/dev/null || true - - echo "" - echo "── Validation ───────────────────────────────────" - - # Check for remaining placeholders - PATTERN="${LB}[A-Z_]*${RB}" - REMAINING=$(grep -rl "$PATTERN" . --include='*.md' --include='*.adoc' --include='*.yml' --include='*.yaml' --include='*.a2ml' --include='*.toml' --include='*.scm' --include='*.ncl' --include='*.nix' --include='*.json' --include='*.sh' 2>/dev/null | grep -v '.git/' | grep -v 'PLACEHOLDERS.md' || true) - if [ -n "$REMAINING" ]; then - echo "WARNING: Remaining placeholders in:" - echo "$REMAINING" | sed 's/^/ /' - echo "" - echo "Run: grep -rn '$LB' . --include='*.md' to inspect" - else - echo "All placeholders replaced successfully!" - fi - - # K9-SVC validation (if available) - if command -v k9-svc >/dev/null 2>&1; then - echo "" - echo "Running k9-svc validation..." - k9-svc validate . 2>/dev/null || true - fi - - echo "" - echo "Done! Next steps:" - echo " 1. Review changes: git diff" - echo " 2. Remove template cruft: rm PLACEHOLDERS.md" - echo " 3. Customize README.adoc for your project" - echo " 4. Commit: git add -A && git commit -m 'feat: initialize from RSR template'" - echo " 5. Push: git remote add origin git@${FORGE}:${OWNER}/${REPO}.git && git push -u origin main" - -# ═══════════════════════════════════════════════════════════════════════════════ -# BUILD & COMPILE -# ═══════════════════════════════════════════════════════════════════════════════ - -# Build the project (debug mode) -build *args: - @echo "Building {{project}} (debug)..." - # TODO: Replace with your build command - # Examples: - # cargo build {{args}} # Rust - # mix compile {{args}} # Elixir - # zig build {{args}} # Zig - # deno task build {{args}} # Deno/ReScript - @echo "Build complete" - -# Build in release mode with optimizations -build-release *args: - @echo "Building {{project}} (release)..." - # TODO: Replace with your release build command - # Examples: - # cargo build --release {{args}} - # MIX_ENV=prod mix compile {{args}} - # zig build -Doptimize=ReleaseFast {{args}} - @echo "Release build complete" - -# Build and watch for changes (requires entr or similar) -build-watch: - @echo "Watching for changes..." - # TODO: Customize file patterns for your language - # Examples: - # find src -name '*.rs' | entr -c just build - # mix compile --force --warnings-as-errors - # deno task dev - -# Clean build artifacts [reversible: rebuild with `just build`] -clean: - @echo "Cleaning..." - # TODO: Customize for your build system - rm -rf target/ _build/ build/ dist/ out/ obj/ bin/ - -# Deep clean including caches [reversible: rebuild] -clean-all: clean - rm -rf .cache .tmp +# Run a filesystem vulnerability scan; fail if Trivy is not installed. +deps-audit: + #!/usr/bin/env bash + set -euo pipefail + command -v trivy >/dev/null 2>&1 || { echo "ERROR: trivy is required for dependency/filesystem auditing" >&2; exit 127; } + trivy fs --severity HIGH,CRITICAL --exit-code 1 --quiet . -# ═══════════════════════════════════════════════════════════════════════════════ -# TEST & QUALITY -# ═══════════════════════════════════════════════════════════════════════════════ - -# Run all tests -test *args: - @echo "Running tests..." - # TODO: Replace with your test command - # Examples: - # cargo test {{args}} - # mix test {{args}} - # zig build test {{args}} - # deno test {{args}} - @echo "Tests passed!" - -# Run tests with verbose output -test-verbose: - @echo "Running tests (verbose)..." - # TODO: Replace with verbose test command - -# Smoke test -test-smoke: - @echo "Smoke test..." - # TODO: Add basic sanity checks - -# Run all quality checks -quality: fmt-check lint test - @echo "All quality checks passed!" +security: security-test deps-audit + @echo "Configured static smoke checks and Trivy scan passed." -# Fix all auto-fixable issues [reversible: git checkout] -fix: fmt - @echo "Fixed all auto-fixable issues" +# Generate an SPDX JSON SBOM only when Syft is available. +sbom: + #!/usr/bin/env bash + set -euo pipefail + command -v syft >/dev/null 2>&1 || { echo "ERROR: syft is required to generate the SBOM" >&2; exit 127; } + mkdir -p docs/security + syft . -o spdx-json > docs/security/sbom.spdx.json + echo "Generated docs/security/sbom.spdx.json" -# ═══════════════════════════════════════════════════════════════════════════════ -# LINT & FORMAT -# ═══════════════════════════════════════════════════════════════════════════════ +# Generate the Justfile cookbook and man page (not all project documentation). +docs: cookbook man + @echo "Generated Justfile task reference and man page." -# Format all source files [reversible: git checkout] -fmt: - @echo "Formatting source files..." - # TODO: Replace with your formatter - # Examples: - # cargo fmt - # mix format - # gleam format - # deno fmt - -# Check formatting without changes -fmt-check: - @echo "Checking formatting..." - # TODO: Replace with your format check - # Examples: - # cargo fmt --check - # mix format --check-formatted - # gleam format --check - -# Run linter -lint: - @echo "Linting source files..." - # TODO: Replace with your linter - # Examples: - # cargo clippy -- -D warnings - # mix credo --strict - # gleam check - -# ═══════════════════════════════════════════════════════════════════════════════ -# RUN & EXECUTE -# ═══════════════════════════════════════════════════════════════════════════════ - -# Run the application -run *args: build - # TODO: Replace with your run command - echo "Run not configured yet" - -# Run with verbose output -run-verbose *args: build - # TODO: Replace with verbose run command - echo "Run not configured yet" - -# Install to user path -install: build-release - @echo "Installing {{project}}..." - # TODO: Replace with your install command - -# ═══════════════════════════════════════════════════════════════════════════════ -# DEPENDENCIES -# ═══════════════════════════════════════════════════════════════════════════════ - -# Install/check all dependencies -deps: - @echo "Checking dependencies..." - # TODO: Replace with your dependency check - # Examples: - # cargo check - # mix deps.get - # gleam deps download - @echo "All dependencies satisfied" - -# Audit dependencies for vulnerabilities -deps-audit: - @echo "Auditing for vulnerabilities..." - # TODO: Replace with your audit command - # Examples: - # cargo audit - # mix audit - @command -v trivy >/dev/null && trivy fs --severity HIGH,CRITICAL --quiet . || true - @echo "Audit complete" - -# ═══════════════════════════════════════════════════════════════════════════════ -# DOCUMENTATION -# ═══════════════════════════════════════════════════════════════════════════════ - -# Generate all documentation -docs: - @mkdir -p docs/generated docs/man - just cookbook - just man - @echo "Documentation generated in docs/" - -# Generate justfile cookbook documentation cookbook: #!/usr/bin/env bash + set -euo pipefail mkdir -p docs - OUTPUT="docs/just-cookbook.adoc" - echo "= {{project}} Justfile Cookbook" > "$OUTPUT" - echo ":toc: left" >> "$OUTPUT" - echo ":toclevels: 3" >> "$OUTPUT" - echo "" >> "$OUTPUT" - echo "Generated: $(date -Iseconds)" >> "$OUTPUT" - echo "" >> "$OUTPUT" - echo "== Recipes" >> "$OUTPUT" - echo "" >> "$OUTPUT" - just --list --unsorted | while read -r line; do - if [[ "$line" =~ ^[[:space:]]+([a-z_-]+) ]]; then - recipe="${BASH_REMATCH[1]}" - echo "=== $recipe" >> "$OUTPUT" - echo "" >> "$OUTPUT" - echo "[source,bash]" >> "$OUTPUT" - echo "----" >> "$OUTPUT" - echo "just $recipe" >> "$OUTPUT" - echo "----" >> "$OUTPUT" - echo "" >> "$OUTPUT" - fi - done - echo "Generated: $OUTPUT" + output="docs/just-cookbook.adoc" + { + echo '= proven-servers Justfile Cookbook' + echo ':toc: left' + echo ':toclevels: 2' + echo + echo 'This file is generated from the configured root Justfile recipes.' + echo + echo '== Available Recipes' + echo + just --list --unsorted + } > "$output" + echo "Generated $output" -# Generate man page man: #!/usr/bin/env bash + set -euo pipefail mkdir -p docs/man - cat > docs/man/{{project}}.1 << EOF - .TH {{project}} 1 "$(date +%Y-%m-%d)" "{{version}}" "{{project}} Manual" + cat > docs/man/proven-servers.1 <<'EOF' + .TH proven-servers 1 "$(date +%Y-%m-%d)" "proven-servers" "User Commands" .SH NAME - {{project}} \- RSR-compliant project - .SH SYNOPSIS - .B just - [recipe] [args...] + proven-servers \- protocol models and FFI prototype sources .SH DESCRIPTION - RSR (Rhodium Standard Repository) project managed with just. - .SH AUTHOR - $(git config user.name 2>/dev/null || echo "Author") <$(git config user.email 2>/dev/null || echo "email")> + This source repository is not a production server distribution. The Justfile provides package-scoped build and test tasks plus static smoke checks. + .SH SEE ALSO + README.adoc(7), QUICKSTART-DEV.adoc(7) EOF - echo "Generated: docs/man/{{project}}.1" - -# ═══════════════════════════════════════════════════════════════════════════════ -# CONTAINERS (stapeln ecosystem — Podman + Chainguard Wolfi) -# ═══════════════════════════════════════════════════════════════════════════════ + echo "Generated docs/man/proven-servers.1" -# Initialise container templates — substitute placeholders with project values -container-init: +# Validate repository-specific metadata locations and synchronized task copies. +validate-rsr: #!/usr/bin/env bash set -euo pipefail - - if [ ! -d "container" ]; then - echo "Error: container/ directory not found." - echo "This repo may not have been created from rsr-template-repo." - exit 1 - fi - - echo "=== Container Template Initialisation ===" - echo "" - - # Load RSR defaults if available - DEFAULTS="${XDG_CONFIG_HOME:-$HOME/.config}/rsr/defaults" - if [ -f "$DEFAULTS" ]; then - echo "Loading defaults from $DEFAULTS" - # shellcheck source=/dev/null - source "$DEFAULTS" - echo "" - fi - - # Prompt for container-specific values - read -rp "Service name (e.g. my-api) [{{project}}]: " _SERVICE_NAME - SERVICE_NAME="${_SERVICE_NAME:-{{project}}}" - - read -rp "Primary port [8080]: " _PORT - PORT="${_PORT:-8080}" - - read -rp "Container registry [ghcr.io/${OWNER:-hyperpolymath}]: " _REGISTRY - REGISTRY="${_REGISTRY:-ghcr.io/${OWNER:-hyperpolymath}}" - - echo "" - echo " Service: $SERVICE_NAME" - echo " Port: $PORT" - echo " Registry: $REGISTRY" - echo "" - read -rp "Proceed? [Y/n] " CONFIRM - [[ "${CONFIRM:-Y}" =~ ^[Nn] ]] && echo "Aborted." && exit 0 - - echo "" - echo "Replacing container placeholders..." - - # Brace tokens as variables (hex escapes avoid just interpolation) - LB=$(printf '\x7b\x7b') - RB=$(printf '\x7d\x7d') - - SED_ARGS=( - -e "s|${LB}SERVICE_NAME${RB}|${SERVICE_NAME}|g" - -e "s|${LB}PORT${RB}|${PORT}|g" - -e "s|${LB}REGISTRY${RB}|${REGISTRY}|g" + required=( + .editorconfig .gitattributes .gitignore mise.toml Justfile + README.adoc LICENSE SECURITY.adoc MAINTAINERS .github/CODEOWNERS + 0-AI-MANIFEST.a2ml .well-known/security.txt + .machine_readable/6a2/AGENTIC.a2ml + .machine_readable/6a2/ECOSYSTEM.a2ml + .machine_readable/6a2/META.a2ml + .machine_readable/6a2/NEUROSYM.a2ml + .machine_readable/6a2/PLAYBOOK.a2ml + .machine_readable/6a2/STATE.a2ml + .machine_readable/6a2/anchor/ANCHOR.a2ml + .machine_readable/rsr-profile.a2ml + .machine_readable/BINDINGS.a2ml + .machine_readable/contractiles/Adjustfile.a2ml + .machine_readable/contractiles/Intentfile.a2ml + .machine_readable/contractiles/Mustfile.a2ml + .machine_readable/contractiles/Trustfile.a2ml + .machine_readable/contractiles/Justfile + .machine_readable/policies/MAINTENANCE-AXES.a2ml + .machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml + .machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml + .machine_readable/ai/README.adoc + .machine_readable/bot_directives/README.adoc + docs/maintenance/MAINTENANCE-CHECKLIST.adoc + docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc + docs/AI-CONVENTIONS.adoc ) - - find container/ -type f | while read -r file; do - if file --brief "$file" | grep -qi 'text\|ascii\|utf'; then - sed -i "${SED_ARGS[@]}" "$file" + missing=0 + for path in "${required[@]}"; do + if [ ! -e "$path" ]; then + printf 'MISSING: %s\n' "$path" >&2 + missing=1 fi done - - echo "Container templates initialised." - echo "" - echo "Next steps:" - echo " 1. Edit container/Containerfile — add your build commands" - echo " 2. Edit container/entrypoint.sh — set your application binary" - echo " 3. Review container/compose.toml — adjust services and volumes" - echo " 4. Build: just container-build" - -# Build container image via cerro-torre pipeline -container-build *args: - #!/usr/bin/env bash - if [ -f "container/ct-build.sh" ]; then - cd container && ./ct-build.sh {{args}} - elif [ -f "container/Containerfile" ]; then - podman build -t {{project}}:latest -f container/Containerfile . - elif [ -f "Containerfile" ]; then - podman build -t {{project}}:latest -f Containerfile . - else - echo "No Containerfile found in container/ or project root" - exit 1 + for name in STATE META ECOSYSTEM AGENTIC NEUROSYM PLAYBOOK; do + if [ -e ".machine_readable/$name.a2ml" ]; then + printf 'DUPLICATE CORE METADATA: .machine_readable/%s.a2ml\n' "$name" >&2 + missing=1 + fi + done + if ! cmp -s Justfile .machine_readable/contractiles/Justfile; then + echo "MISMATCH: .machine_readable/contractiles/Justfile is not synchronized with Justfile" >&2 + missing=1 fi + [ "$missing" -eq 0 ] || exit 1 + echo "Repository metadata paths and Justfile copy are consistent." -# Verify compose configuration -container-verify: +# Validate only basic required fields; this is not a complete A2ML parser. +validate-state: #!/usr/bin/env bash - if [ ! -f "container/compose.toml" ]; then - echo "No container/compose.toml found" - exit 1 - fi - cd container - if command -v selur-compose &>/dev/null; then - selur-compose verify - else - echo "selur-compose not found, falling back to podman compose" - podman compose --file compose.toml config - fi - -# Start container stack -container-up *args: + set -euo pipefail + state=.machine_readable/6a2/STATE.a2ml + test -f "$state" + grep -q '^\[metadata\]$' "$state" + grep -q '^project = "proven-servers"$' "$state" + grep -Eq '^last-updated = "[0-9]{4}-[0-9]{2}-[0-9]{2}"$' "$state" + grep -q '^\[position\]$' "$state" + grep -Eq '^phase = "[^"]+"$' "$state" + echo "STATE.a2ml has the required project and position fields (syntax not fully parsed)." + +# Ensure AI-facing onboarding warns about scope and points to real developer steps. +validate-onboarding: #!/usr/bin/env bash - if [ ! -f "container/compose.toml" ]; then - echo "No container/compose.toml found" + set -euo pipefail + guide=docs/AI_INSTALLATION_GUIDE.adoc + test -f "$guide" + grep -q 'not a production server' "$guide" + grep -q 'QUICKSTART-DEV.adoc' "$guide" + if grep -q 'TODO-AI-INSTALL' "$guide"; then + echo "ERROR: obsolete AI-install placeholders remain in $guide" >&2 exit 1 fi - cd container - if command -v selur-compose &>/dev/null; then - selur-compose up {{args}} - else - podman compose --file compose.toml up {{args}} - fi + echo "AI-assisted developer onboarding states its deployment boundary." -# Stop container stack -container-down: - #!/usr/bin/env bash - cd container 2>/dev/null || { echo "No container/ directory"; exit 1; } - if command -v selur-compose &>/dev/null; then - selur-compose down - else - podman compose --file compose.toml down - fi - -# Sign and verify container bundle (build + pack + sign + verify) -container-sign: - #!/usr/bin/env bash - if [ -f "container/ct-build.sh" ]; then - cd container && ./ct-build.sh - else - echo "No container/ct-build.sh found" - exit 1 - fi +validate: validate-rsr validate-state validate-onboarding -# Push signed bundle to registry -container-push: +state-touch: #!/usr/bin/env bash - if [ -f "container/ct-build.sh" ]; then - cd container && ./ct-build.sh --push - else - echo "No container/ct-build.sh found — falling back to podman push" - podman push {{project}}:latest - fi - -# Run container interactively (for debugging) -container-run *args: - podman run --rm -it {{project}}:latest {{args}} + set -euo pipefail + state=.machine_readable/6a2/STATE.a2ml + sed -i "s/^last-updated = \"[^"]*\"/last-updated = \"$(date +%Y-%m-%d)\"/" "$state" + echo "Updated $state timestamp." -# ═══════════════════════════════════════════════════════════════════════════════ -# CI & AUTOMATION -# ═══════════════════════════════════════════════════════════════════════════════ +state-phase: + @sed -n 's/^phase = "\(.*\)"/\1/p' .machine_readable/6a2/STATE.a2ml | head -1 -# Run full CI pipeline locally -ci: deps quality - @echo "CI pipeline complete!" +# Run all configured local quality gates; requires the declared toolchains. +ci: quality security + @echo "Configured local CI tasks passed." -# Install git hooks install-hooks: - @mkdir -p .git/hooks - @cat > .git/hooks/pre-commit << 'HOOKEOF' - #!/bin/bash - just fmt-check || exit 1 - just lint || exit 1 - HOOKEOF - @chmod +x .git/hooks/pre-commit - @echo "Git hooks installed" - -# ═══════════════════════════════════════════════════════════════════════════════ -# SECURITY -# ═══════════════════════════════════════════════════════════════════════════════ - -# Run security audit -security: deps-audit - @echo "=== Security Audit ===" - @command -v trivy >/dev/null && trivy fs --severity HIGH,CRITICAL . || true - @echo "Security audit complete" - -# Generate SBOM -sbom: - @mkdir -p docs/security - @command -v syft >/dev/null && syft . -o spdx-json > docs/security/sbom.spdx.json || echo "syft not found" - -# ═══════════════════════════════════════════════════════════════════════════════ -# VALIDATION & COMPLIANCE -# ═══════════════════════════════════════════════════════════════════════════════ - -# Validate RSR compliance -validate-rsr: #!/usr/bin/env bash - echo "=== RSR Compliance Check ===" - MISSING="" - for f in .editorconfig .gitignore Justfile README.adoc LICENSE; do - [ -f "$f" ] || MISSING="$MISSING $f" - done - for f in .machine_readable/STATE.a2ml .machine_readable/META.a2ml .machine_readable/ECOSYSTEM.a2ml .machine_readable/anchors/ANCHOR.a2ml .machine_readable/policies/MAINTENANCE-AXES.a2ml .machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml .machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml; do - [ -f "$f" ] || MISSING="$MISSING $f" - done - for f in docs/maintenance/MAINTENANCE-CHECKLIST.md docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc; do - [ -f "$f" ] || MISSING="$MISSING $f" - done - if [ -f ".machine_readable/META.a2ml" ]; then - grep -q 'axis-1 = "must > intend > like"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:axis-1" - grep -q 'axis-2 = "corrective > adaptive > perfective"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:axis-2" - grep -q 'axis-3 = "systems > compliance > effects"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:axis-3" - grep -q 'scoping-first = true' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:scoping-first" - grep -q 'idris-unsound-scan = "believe_me/assert_total"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:idris-unsound-scan" - grep -q 'audit-focus = "systems in place, documentation explains actual state, safety/security accounted for, observed effects reviewed"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:audit-focus" - grep -q 'compliance-focus = "seams/compromises/exception register, bounded exceptions, anti-drift checks"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:compliance-focus" - grep -q 'effects-evidence = "benchmark execution/results and maintainer status dialogue/review"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:effects-evidence" - grep -q 'compliance-tooling = "panic-attack"' .machine_readable/policies/MAINTENANCE-AXES.a2ml || MISSING="$MISSING MAINTENANCE-AXES.a2ml:compliance-tooling" - grep -q 'effects-tooling = "ecological checking with sustainabot guidance"' .machine_readable/policies/MAINTENANCE-AXES.a2ml || MISSING="$MISSING MAINTENANCE-AXES.a2ml:effects-tooling" - grep -q 'source-human = "docs/maintenance/MAINTENANCE-CHECKLIST.md"' .machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml || MISSING="$MISSING MAINTENANCE-CHECKLIST.a2ml:source-human" - grep -q 'source-human = "docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc"' .machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml || MISSING="$MISSING SOFTWARE-DEVELOPMENT-APPROACH.a2ml:source-human" - fi - if [ -n "$MISSING" ]; then - echo "MISSING:$MISSING" - exit 1 - fi - echo "RSR compliance: PASS" + set -euo pipefail + hooks_dir="$(git rev-parse --git-path hooks)" + mkdir -p "$hooks_dir" + cat > "$hooks_dir/pre-commit" <<'HOOK' + #!/usr/bin/env bash + set -euo pipefail + just fmt-check + just lint + HOOK + chmod +x "$hooks_dir/pre-commit" + echo "Installed pre-commit hook at $hooks_dir/pre-commit" -# Validate STATE.a2ml syntax -validate-state: - @if [ -f ".machine_readable/STATE.a2ml" ]; then \ - grep -q '^\[metadata\]' .machine_readable/STATE.a2ml && \ - grep -q 'project\s*=' .machine_readable/STATE.a2ml && \ - echo "STATE.a2ml: valid" || echo "STATE.a2ml: INVALID (missing required sections)"; \ - else \ - echo "No .machine_readable/STATE.a2ml found"; \ - fi +# Run the standards scanner only when installed; absence is an explicit error. +assail: + @command -v panic-attack >/dev/null 2>&1 || { echo "ERROR: panic-attack is required for this scan" >&2; exit 127; } + panic-attack assail . -# Validate AI installation guide completeness (finishbot pre-release check) -validate-ai-install: +doctor: #!/usr/bin/env bash - echo "=== AI Installation Guide Check ===" - GUIDE="docs/AI_INSTALLATION_GUIDE.adoc" - README="README.adoc" - ERRORS=0 - - # Check guide exists - if [ ! -f "$GUIDE" ]; then - echo "MISSING: $GUIDE (create from template: docs/AI_INSTALLATION_GUIDE.adoc)" - ERRORS=$((ERRORS + 1)) - else - # Check for unfilled TODO markers - TODOS=$(grep -c '\[TODO-AI-INSTALL' "$GUIDE" 2>/dev/null || true) - if [ "$TODOS" -gt 0 ]; then - echo "INCOMPLETE: $GUIDE has $TODOS unfilled [TODO-AI-INSTALL] markers:" - grep -n '\[TODO-AI-INSTALL' "$GUIDE" | head -10 - ERRORS=$((ERRORS + 1)) + set -euo pipefail + missing=0 + for tool in git just idris2 zig; do + if command -v "$tool" >/dev/null 2>&1; then + printf '[OK] %s: %s\n' "$tool" "$(command -v "$tool")" else - echo "$GUIDE: complete (no TODO markers)" - fi - - # Check AI implementation section exists - if ! grep -q 'ai-implementation' "$GUIDE" 2>/dev/null; then - echo "MISSING: [[ai-implementation]] anchor in $GUIDE" - ERRORS=$((ERRORS + 1)) + printf '[MISSING] %s\n' "$tool" >&2 + missing=1 fi + done + printf 'Branch: %s\n' "$(git branch --show-current)" + printf 'Working tree: %s\n' "$(if [ -z "$(git status --porcelain)" ]; then echo clean; else echo modified; fi)" + exit "$missing" - # Check privacy notice exists - if ! grep -qi 'privacy' "$GUIDE" 2>/dev/null; then - echo "MISSING: Privacy notice in $GUIDE" - ERRORS=$((ERRORS + 1)) - fi - - # Check install commands exist (not just placeholders) - if ! grep -q 'git clone' "$GUIDE" 2>/dev/null; then - echo "WARNING: No git clone command found in $GUIDE -- install commands may be incomplete" - fi - fi - - # Check README has AI install section - if [ -f "$README" ]; then - if ! grep -qi 'AI-Assisted Installation' "$README" 2>/dev/null; then - echo "MISSING: AI-Assisted Installation section in $README" - echo " Copy from docs/AI-INSTALL-README-SECTION.adoc" - ERRORS=$((ERRORS + 1)) - fi - - # Check README for unfilled TODO markers - README_TODOS=$(grep -c '\[TODO-AI-INSTALL' "$README" 2>/dev/null || true) - if [ "$README_TODOS" -gt 0 ]; then - echo "INCOMPLETE: $README has $README_TODOS unfilled [TODO-AI-INSTALL] markers" - ERRORS=$((ERRORS + 1)) - fi - fi - - if [ "$ERRORS" -gt 0 ]; then - echo "" - echo "AI install guide: FAIL ($ERRORS issues)" - exit 1 - fi - echo "AI install guide: PASS" - -# Full validation suite -validate: validate-rsr validate-state validate-ai-install - @echo "All validations passed!" - -# ═══════════════════════════════════════════════════════════════════════════════ -# STATE MANAGEMENT -# ═══════════════════════════════════════════════════════════════════════════════ - -# Update STATE.a2ml timestamp -state-touch: - @if [ -f ".machine_readable/STATE.a2ml" ]; then \ - sed -i 's/last-updated = "[^"]*"/last-updated = "'"$(date +%Y-%m-%d)"'"/' .machine_readable/STATE.a2ml && \ - echo "STATE.a2ml timestamp updated"; \ - fi - -# Show current phase from STATE.a2ml -state-phase: - @grep -oP 'phase\s*=\s*"\K[^"]+' .machine_readable/STATE.a2ml 2>/dev/null | head -1 || echo "unknown" - -# ═══════════════════════════════════════════════════════════════════════════════ -# GUIX & NIX -# ═══════════════════════════════════════════════════════════════════════════════ - -# Enter Guix development shell (primary) -guix-shell: - guix shell -D -f build/guix.scm - -# Build with Guix -guix-build: - guix build -f build/guix.scm - -# Enter Nix development shell (fallback) -nix-shell: - @if [ -f "flake.nix" ]; then nix develop; else echo "No flake.nix"; fi - -# ═══════════════════════════════════════════════════════════════════════════════ -# HYBRID AUTOMATION -# ═══════════════════════════════════════════════════════════════════════════════ - -# Run local automation tasks -automate task="all": - #!/usr/bin/env bash - case "{{task}}" in - all) just fmt && just lint && just test && just docs && just state-touch ;; - cleanup) just clean && find . -name "*.orig" -delete && find . -name "*~" -delete ;; - update) just deps && just validate ;; - *) echo "Unknown: {{task}}. Use: all, cleanup, update" && exit 1 ;; - esac - -# ═══════════════════════════════════════════════════════════════════════════════ -# COMBINATORIC MATRIX RECIPES -# ═══════════════════════════════════════════════════════════════════════════════ - -# Build matrix: [debug|release] x [target] x [features] -build-matrix mode="debug" target="" features="": - @echo "Build matrix: mode={{mode}} target={{target}} features={{features}}" - -# Test matrix: [unit|integration|e2e|all] x [verbosity] x [parallel] -test-matrix suite="unit" verbosity="normal" parallel="true": - @echo "Test matrix: suite={{suite}} verbosity={{verbosity}} parallel={{parallel}}" - -# Container matrix: [build|run|push|shell|scan] x [registry] x [tag] -container-matrix action="build" registry="ghcr.io/hyperpolymath" tag="latest": - @echo "Container matrix: action={{action}} registry={{registry}} tag={{tag}}" - -# CI matrix: [lint|test|build|security|all] x [quick|full] -ci-matrix stage="all" depth="quick": - @echo "CI matrix: stage={{stage}} depth={{depth}}" - -# Show all matrix combinations -combinations: - @echo "=== Combinatoric Matrix Recipes ===" - @echo "" - @echo "Build Matrix: just build-matrix [debug|release] [target] [features]" - @echo "Test Matrix: just test-matrix [unit|integration|e2e|all] [verbosity] [parallel]" - @echo "Container: just container-matrix [build|run|push|shell|scan] [registry] [tag]" - @echo "CI Matrix: just ci-matrix [lint|test|build|security|all] [quick|full]" - -# ═══════════════════════════════════════════════════════════════════════════════ -# VERSION CONTROL -# ═══════════════════════════════════════════════════════════════════════════════ - -# Show git status status: @git status --short -# Show recent commits log count="20": @git log --oneline -{{count}} -# Generate CHANGELOG.md with git-cliff -changelog: - @command -v git-cliff >/dev/null || { echo "git-cliff not found — install: cargo install git-cliff"; exit 1; } - git cliff --output CHANGELOG.md - @echo "Generated CHANGELOG.md" - -# Preview changelog for unreleased commits (does not write) -changelog-preview: - @command -v git-cliff >/dev/null || { echo "git-cliff not found — install: cargo install git-cliff"; exit 1; } - git cliff --unreleased --strip header - -# Tag a new release (usage: just release-tag 1.2.3) -release-tag version: - #!/usr/bin/env bash - TAG="v{{version}}" - if git rev-parse "$TAG" >/dev/null 2>&1; then - echo "Tag $TAG already exists" - exit 1 - fi - just changelog - git add CHANGELOG.md - git commit -m "chore(release): prepare $TAG" - git tag -a "$TAG" -m "Release $TAG" - echo "Created tag $TAG — push with: git push origin main --tags" - -# ═══════════════════════════════════════════════════════════════════════════════ -# UTILITIES -# ═══════════════════════════════════════════════════════════════════════════════ - -# Count lines of code -loc: - @find . \( -name "*.rs" -o -name "*.ex" -o -name "*.exs" -o -name "*.res" -o -name "*.gleam" -o -name "*.zig" -o -name "*.idr" -o -name "*.hs" -o -name "*.ncl" -o -name "*.scm" -o -name "*.adb" -o -name "*.ads" \) -not -path './target/*' -not -path './_build/*' 2>/dev/null | xargs wc -l 2>/dev/null | tail -1 || echo "0" - -# Show TODO comments -todos: - @grep -rn "TODO\|FIXME\|HACK\|XXX" --include="*.rs" --include="*.ex" --include="*.res" --include="*.gleam" --include="*.zig" --include="*.idr" --include="*.hs" . 2>/dev/null || echo "No TODOs" - -# Open in editor -edit: - ${EDITOR:-code} . - -# Run panic-attacker pre-commit scan (whole repo) -assail: - @command -v panic-attack >/dev/null 2>&1 && panic-attack assail . || echo "panic-attack not found — install from https://github.com/hyperpolymath/panic-attacker" - -# Run panic-attack unified-api-adapter surface scan on the nesy-solver-api V connector. -# Uses connectors/proven-nesy-solver-api/v/panic-attack.toml for surface-specific -# thresholds (REST + VerisimDB held to 85 robustness; others to 75). -assail-unified-api-adapter: - @command -v panic-attack >/dev/null 2>&1 || { echo "panic-attack not found — install from https://github.com/hyperpolymath/panic-attacker"; exit 1; } - @echo "Scanning unified-api-adapter connector (16 surfaces)..." - @cd connectors/proven-nesy-solver-api/v && panic-attack assail . --config panic-attack.toml - -# Self-diagnostic — checks dependencies, permissions, paths -doctor: - @echo "Running diagnostics for proven-servers..." - @echo "Checking required tools..." - @command -v just >/dev/null 2>&1 && echo " [OK] just" || echo " [FAIL] just not found" - @command -v git >/dev/null 2>&1 && echo " [OK] git" || echo " [FAIL] git not found" - @echo "Checking for hardcoded paths..." - @grep -rn '$HOME\|$ECLIPSE_DIR' --include='*.rs' --include='*.ex' --include='*.res' --include='*.gleam' --include='*.sh' . 2>/dev/null | head -5 || echo " [OK] No hardcoded paths" - @echo "Diagnostics complete." - -# Auto-repair common issues -heal: - @echo "Attempting auto-repair for proven-servers..." - @echo "Fixing permissions..." - @find . -name "*.sh" -exec chmod +x {} \; 2>/dev/null || true - @echo "Cleaning stale caches..." - @rm -rf .cache/stale 2>/dev/null || true - @echo "Repair complete." - -# Guided tour of key features tour: - @echo "=== proven-servers Tour ===" - @echo "" - @echo "1. Project structure:" - @ls -la - @echo "" - @echo "2. Available commands: just --list" - @echo "" - @echo "3. Read README.adoc for full overview" - @echo "4. Read EXPLAINME.adoc for architecture decisions" - @echo "5. Run 'just doctor' to check your setup" - @echo "" - @echo "Tour complete! Try 'just --list' to see all available commands." - -# Open feedback channel with diagnostic context + @echo "Read README.adoc, QUICKSTART-DEV.adoc, and the package-local README before building a component." + help-me: - @echo "=== proven-servers Help ===" - @echo "Platform: $(uname -s) $(uname -m)" - @echo "Shell: $SHELL" - @echo "" - @echo "To report an issue:" - @echo " https://github.com/hyperpolymath/proven-servers/issues/new" - @echo "" - @echo "Include the output of 'just doctor' in your report." + @echo "Issues: https://github.com/hyperpolymath/proven-servers/issues/new" + @echo "Include the exact package, commit, tool versions, command, and complete output." +todos: + @git grep -n -E 'TODO|FIXME|XXX|HACK|STUB|PARTIAL' -- ':!PLACEHOLDERS.adoc' || true -# Print the current CRG grade (reads from READINESS.md '**Current Grade:** X' line) crg-grade: - @grade=$$(grep -oP '(?<=\*\*Current Grade:\*\* )[A-FX]' READINESS.md 2>/dev/null | head -1); \ - [ -z "$$grade" ] && grade="X"; \ - echo "$$grade" + @sed -n 's/^\*\*Current Grade:\*\* \([A-FX]\).*/\1/p' READINESS.adoc | head -1 -# Generate a shields.io badge markdown for the current CRG grade -# Looks for '**Current Grade:** X' in READINESS.md; falls back to X crg-badge: - @grade=$$(grep -oP '(?<=\*\*Current Grade:\*\* )[A-FX]' READINESS.md 2>/dev/null | head -1); \ - [ -z "$$grade" ] && grade="X"; \ - case "$$grade" in \ - A) color="brightgreen" ;; B) color="green" ;; C) color="yellow" ;; \ - D) color="orange" ;; E) color="red" ;; F) color="critical" ;; \ - *) color="lightgrey" ;; esac; \ - echo "[![CRG $$grade](https://img.shields.io/badge/CRG-$$grade-$$color?style=flat-square)](https://github.com/hyperpolymath/standards/tree/main/component-readiness-grades)" - -secret-scan-trufflehog: - @command -v trufflehog >/dev/null && trufflehog filesystem . --only-verified || true + #!/usr/bin/env bash + set -euo pipefail + grade="$(sed -n 's/^\*\*Current Grade:\*\* \([A-FX]\).*/\1/p' READINESS.adoc | head -1)" + [ -n "$grade" ] || grade=X + case "$grade" in + A) color=brightgreen ;; B) color=green ;; C) color=yellow ;; + D) color=orange ;; E) color=red ;; F) color=critical ;; + *) color=lightgrey ;; + esac + printf '[![CRG %s](https://img.shields.io/badge/CRG-%s-%s?style=flat-square)](https://github.com/hyperpolymath/standards/tree/main/component-readiness-grades)\n' "$grade" "$grade" "$color" + +# This source tree is not a deployable service: refuse container operations +# until a real executable target, health endpoint, and image tests are added. +container-build container-verify container-up container-down container-sign container-push container-run: + @echo "ERROR: container deployment is disabled; this repository has no deployable server binary." >&2 + @exit 2 diff --git a/.machine_readable/contractiles/Mustfile.a2ml b/.machine_readable/contractiles/Mustfile.a2ml index 55f8ab48..2fecfacd 100644 --- a/.machine_readable/contractiles/Mustfile.a2ml +++ b/.machine_readable/contractiles/Mustfile.a2ml @@ -1,102 +1,78 @@ # SPDX-License-Identifier: MPL-2.0 -# Mustfile — Physical state contract for rsr-template-repo -# Author: Jonathan D.A. Jewell +# Mustfile — repository-specific physical-state invariants. # -# What MUST be true about this repository. Hard requirements. -# Run with: must check -# Fix with: must fix (where a deterministic fix exists) +# The configured local runner is `just must-check` (from contractile.just). @abstract: -Physical-state invariants for rsr-template-repo. This is the canonical -RSR template repository. These are hard requirements — CI and pre-commit -hooks fail if any check fails. +Hard, path-specific repository invariants for proven-servers. A successful +path/policy check does not establish compiler correctness, ABI conformance, +production readiness, or security certification. @end -## File Presence +## Required Files ### license-present -- description: LICENSE file must exist +- description: MPL-2.0 license file is present - run: test -f LICENSE - severity: critical -### readme-present -- description: README.adoc must exist +### project-readme +- description: Human-facing repository overview is present - run: test -f README.adoc - severity: critical -### security-policy -- description: SECURITY.md must exist -- run: test -f SECURITY.md +### security-guidance +- description: Security policy and RFC 9116 contact file are present +- run: test -f SECURITY.adoc && test -f .well-known/security.txt - severity: critical +### maintainer-governance +- description: Maintainer list and ownership rules are present +- run: test -f MAINTAINERS && test -f .github/CODEOWNERS +- severity: high + ### ai-manifest -- description: 0-AI-MANIFEST.a2ml must exist +- description: Repository AI manifest is present - run: test -f 0-AI-MANIFEST.a2ml -- severity: critical - -### governance-docs -- description: GOVERNANCE.adoc, MAINTAINERS.adoc, CODEOWNERS must exist -- run: test -f GOVERNANCE.adoc && test -f MAINTAINERS.adoc && test -f .github/CODEOWNERS -- severity: critical - -### machine-readable-dir -- description: .machine_readable/ directory must exist -- run: test -d .machine_readable -- severity: critical - -## Directory Structure - -### contractiles-complete -- description: All required contractile directories exist -- run: test -d .machine_readable/contractiles && test -d .machine_readable/contractiles/bust && test -d .machine_readable/contractiles/dust -- severity: critical - -### contractiles-files-present -- description: All four primary contractile files exist -- run: test -f .machine_readable/contractiles/Intentfile.a2ml && test -f .machine_readable/contractiles/Mustfile.a2ml && test -f .machine_readable/contractiles/Trustfile.a2ml && test -f .machine_readable/contractiles/Adjustfile.a2ml -- severity: critical +- severity: high -### bust-dust-files-present -- description: Bustfile and Dustfile exist in their directories -- run: test -f .machine_readable/contractiles/bust/Bustfile.a2ml && test -f .machine_readable/contractiles/dust/Dustfile.a2ml -- severity: critical +### task-runner +- description: Root Justfile and its machine-readable snapshot are synchronized +- run: cmp -s Justfile .machine_readable/contractiles/Justfile +- severity: high -### six-directory-present -- description: 6a2 directory exists with required files -- run: test -d .machine_readable/6a2 && test -f .machine_readable/6a2/META.a2ml && test -f .machine_readable/6a2/ECOSYSTEM.a2ml && test -f .machine_readable/6a2/STATE.a2ml && test -f .machine_readable/6a2/PLAYBOOK.a2ml && test -f .machine_readable/6a2/AGENTIC.a2ml && test -f .machine_readable/6a2/NEUROSYM.a2ml -- severity: critical +## Machine-Readable Structure -### anchors-directory -- description: anchors directory exists in 6a2 -- run: test -d .machine_readable/6a2/anchors -- severity: warning +### canonical-core-metadata +- description: Six core A2ML files and anchor use the canonical 6a2 paths +- run: test -f .machine_readable/6a2/STATE.a2ml && test -f .machine_readable/6a2/META.a2ml && test -f .machine_readable/6a2/ECOSYSTEM.a2ml && test -f .machine_readable/6a2/AGENTIC.a2ml && test -f .machine_readable/6a2/NEUROSYM.a2ml && test -f .machine_readable/6a2/PLAYBOOK.a2ml && test -f .machine_readable/6a2/anchor/ANCHOR.a2ml +- severity: high -### self-validating-structure -- description: self-validating directory has k9-svc and examples -- run: test -d .machine_readable/self-validating && test -d .machine_readable/self-validating/k9-svc && test -d .machine_readable/self-validating/examples -- severity: warning +### no-duplicate-core-metadata +- description: Core A2ML files are not duplicated directly under .machine_readable/ +- run: test ! -e .machine_readable/STATE.a2ml && test ! -e .machine_readable/META.a2ml && test ! -e .machine_readable/ECOSYSTEM.a2ml +- severity: high -## Template Integrity +### contractiles-present +- description: Project Must, Trust, Intent, and Adjust files are present +- run: test -f .machine_readable/contractiles/Mustfile.a2ml && test -f .machine_readable/contractiles/Trustfile.a2ml && test -f .machine_readable/contractiles/Intentfile.a2ml && test -f .machine_readable/contractiles/Adjustfile.a2ml +- severity: high -### no-placeholder-values -- description: No placeholder values remain in template files -- run: test -z "$(grep -r '{{' .machine_readable/contractiles/ 2>/dev/null)" -- severity: critical -- notes: All placeholders must be substituted when copying this template +## Configured Checks -### template-readonly -- description: Template marker files are not modified -- run: grep -q 'RSR_TEMPLATE_DO_NOT_EDIT' .machine_readable/0.1-AI-MANIFEST.a2ml -- severity: warning +### binding-registry +- description: The on-disk binding directory inventory matches the explicit registry +- run: bash tools/check-binding-policy.sh +- severity: high -## Git State +### static-security-smoke +- description: Selected source-pattern security smoke checks run +- run: bash tests/aspect/security_test.sh +- severity: advisory +- notes: This is not a security certification or runtime test. -### no-untracked-contractiles -- description: All contractile files are tracked in git -- run: test -z "$(git ls-files -o --exclude-standard .machine_readable/contractiles/ 2>/dev/null)" -- severity: critical +## Explicit Non-Requirements -### signed-commits -- description: All commits must be signed -- run: git verify-commit HEAD -- severity: critical +- No container deployment is enabled until a runnable application and runtime tests exist. +- No signature requirement is asserted unless a repository policy and signing workflow enforce it. +- Package-specific generated headers and compiler targets are validated only by their relevant package tests. diff --git a/.machine_readable/contractiles/README.adoc b/.machine_readable/contractiles/README.adoc index 952a247b..2eb27faf 100644 --- a/.machine_readable/contractiles/README.adoc +++ b/.machine_readable/contractiles/README.adoc @@ -1,21 +1,21 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Contractiles Template Set +// Copyright (c) 2026 Jonathan D.A. Jewell += Repository Contractiles :toc: -:sectnums: -This directory contains the generalized contractiles templates. Copy the `.machine_readable/contractiles/` directory into a new repo to establish a consistent operational, validation, trust, recovery, and intent framework. +The contractiles in this directory are customized for `proven-servers`; they +are not unmodified templates copied from `rsr-template-repo`. -== Fill-In Instructions +== Files -1. Update the Mustfile to reflect your real invariants (paths, schema versions, ports). -2. Replace Trustfile.hs placeholders with your actual key paths and verification commands. -3. Adjust Dustfile handlers to match your rollback and recovery tooling. -4. Update Intentfile to mirror the roadmap you want the system to evolve toward. +* `Mustfile.a2ml` — path and policy invariants +* `Trustfile.a2ml` — evidence boundaries and trust limitations +* `Intentfile.a2ml` — declared next work and status +* `Adjustfile.a2ml` — advisory drift checks +* `Justfile` — synchronized snapshot of the root Justfile -== Contents - -* `must/Mustfile` - required invariants and validations. -* `trust/Trustfile.hs` - cryptographic verification steps. -* `dust/Dustfile` - rollback and recovery semantics. -* `lust/Intentfile` - future intent and roadmap direction. +The imported `contractile.just` adds non-destructive wrappers such as +`just must-check`, `just trust-smoke`, `just dust-status`, and +`just intend-list`. No bulk rollback command is provided. Running these checks +does not establish formal proof, native ABI conformance, release readiness, or +security certification. diff --git a/.machine_readable/contractiles/Trustfile.a2ml b/.machine_readable/contractiles/Trustfile.a2ml index e2028b56..ba99e171 100644 --- a/.machine_readable/contractiles/Trustfile.a2ml +++ b/.machine_readable/contractiles/Trustfile.a2ml @@ -1,88 +1,53 @@ # SPDX-License-Identifier: MPL-2.0 -# Trustfile — Trust boundaries and integrity invariants for rsr-template-repo -# Author: Jonathan D.A. Jewell -# -# Defines what LLM/SLM agents are trusted to do without asking, and -# integrity invariants that verify the repo has not been tampered with. +# Trustfile — evidence and trust boundaries for proven-servers. @abstract: -Trust boundaries and integrity checks for rsr-template-repo. This file -combines the trust-level definitions from the original TRUST.contractile -with the integrity invariants from the old Trustfile.a2ml. It defines -what AI agents may do autonomously and what requires human approval, -plus checks that verify repository integrity. +Defines which repository claims require external evidence and which local +checks are only heuristics. These entries do not imply a cryptographic trust +chain, signed commits, or a complete secret-scanning service. @end -## Trust Levels +## Evidence Boundaries -The rsr-template-repo operates at trust level: maximal +### model-build-is-not-ffi-proof +- description: Idris2 model verification is not evidence that independent Zig, C-header, or binding sources conform +- required-evidence: Build the exact Idris2 package and execute generated-interface/native boundary tests +- status: policy -Trust levels: -- maximal: Agent may read, build, test, lint, format, heal freely. - Only destructive/external actions require approval. -- standard: Agent may read and build. Test/lint need approval. -- restricted: Agent may read only. All modifications need approval. -- minimal: Agent may read specific files only. Everything else blocked. +### source-inventory-is-not-binding-support +- description: Binding directories or declarations do not prove compilation, linking, or runtime support +- required-evidence: Build, link, and run each binding against its intended native library and record toolchain versions +- status: policy -Current trust level: maximal +### smoke-check-scope +- description: Shell grep/source-shape checks are heuristics only +- required-evidence: Use compiler-backed tests and protocol vectors for operational claims +- status: policy -## Integrity Invariants +### fail-closed-security-operations +- description: Authentication, MFA, signing, validation, or token operations must not report success without required data and an implementation +- required-evidence: Negative tests for invalid and missing credentials/material, with state/output checks +- status: policy -### Secrets +## Tool Availability -#### no-secrets-committed -- description: No credential files in repo -- run: test ! -f .env && test ! -f credentials.json && test ! -f .env.local && test ! -f .env.production -- severity: critical +### external-scanners +- description: Trivy, Syft, panic-attack, and other external scanners are optional local tools +- required-action: Report not-installed tools explicitly; do not convert absence into a successful scan +- status: availability varies -#### no-private-keys -- description: No private key files committed -- run: "! find . -name '*.pem' -o -name '*.key' -o -name 'id_rsa' -o -name 'id_ed25519' 2>/dev/null | grep -v node_modules | head -1 | grep -q ." -- severity: critical +### signed-commits +- description: This repository does not currently require every commit to be signed +- status: not-enforced -#### no-tokens-in-source -- description: No hardcoded API tokens in source -- run: "! grep -rE '(api[_-]?key|secret|token|password)\s*[:=]\s*[\"'\\''][A-Za-z0-9]{16,}' --include='*.js' --include='*.ts' --include='*.res' --include='*.py' . 2>/dev/null | grep -v node_modules | head -1 | grep -q ." -- severity: critical +## Working-Tree Safety -## Provenance +### no-automatic-rollback +- description: Contractile task helpers must not discard arbitrary working-tree changes +- required-action: Use reviewed, path-scoped Git operations; no bulk checkout/reset helper is provided +- status: policy -#### author-correct -- description: Git author matches expected identity -- run: "git log -1 --format='%ae' | grep -qE '(hyperpolymath|j\\.d\\.a\\.jewell)'" -- severity: warning - -#### license-content -- description: LICENSE contains expected identifier -- run: grep -q 'PMPL\|MPL\|MIT\|Apache\|LGPL' LICENSE -- severity: warning - -## Template-Specific Trust - -### template-files-readonly -- description: Template scaffold files should not be modified except by maintainer -- run: test -z "$(git status --short .machine_readable/ 2>/dev/null | grep -v '^??' || true)" -- severity: advisory -- notes: Changes to template files require careful review - -### trust-deny-areas -- description: Sensitive areas from INTENT.contractile require explicit approval -- run: echo "Check .machine_readable/ contractiles and governance docs" -- severity: advisory -- areas: - - .machine_readable/ - - GOVERNANCE.adoc - - MAINTAINERS.adoc - - .github/CODEOWNERS - -## Container Security - -#### container-images-pinned -- description: Containerfile uses pinned base images -- run: test ! -f Containerfile || grep -q 'cgr.dev\|@sha256:' Containerfile -- severity: warning - -#### no-dockerfile -- description: No Dockerfile (use Containerfile) -- run: test ! -f Dockerfile -- severity: warning +### deployment-disabled +- description: Container and deployment artefacts are scaffolding, not a validated service image +- required-action: Do not build, sign, push, or deploy until a runnable target and tests are established +- status: policy diff --git a/.machine_readable/policies/MAINTENANCE-AXES.a2ml b/.machine_readable/policies/MAINTENANCE-AXES.a2ml index 2428ef74..1c9291ef 100644 --- a/.machine_readable/policies/MAINTENANCE-AXES.a2ml +++ b/.machine_readable/policies/MAINTENANCE-AXES.a2ml @@ -1,28 +1,34 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# Canonical maintenance governance model +# MAINTENANCE-AXES.a2ml — repository maintenance governance model. [metadata] -version = "1.0.0" -last-updated = "2026-03-02" +version = "1.1.0" +last-updated = "2026-09-27" scope = "repo" [discovery] human-entrypoints = [ "README.adoc", - "docs/maintenance/MAINTENANCE-CHECKLIST.md", + "ROADMAP.adoc", + "READINESS.adoc", + "PROOF-NEEDS.adoc", + "docs/maintenance/MAINTENANCE-CHECKLIST.adoc", "docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc", + "SECURITY.adoc", ] machine-entrypoints = [ + ".machine_readable/6a2/STATE.a2ml", + ".machine_readable/6a2/META.a2ml", + ".machine_readable/6a2/ECOSYSTEM.a2ml", + ".machine_readable/6a2/AGENTIC.a2ml", + ".machine_readable/6a2/anchor/ANCHOR.a2ml", ".machine_readable/policies/MAINTENANCE-AXES.a2ml", ".machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml", ".machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml", - ".machine_readable/META.a2ml", - ".machine_readable/ai/README.adoc", - ".machine_readable/bot_directives/README.scm", + ".machine_readable/BINDINGS.a2ml", ] -bots = ["hypatia", "gitbot-fleet", "repo visitors"] [axes] axis-1 = "must > intend > like" @@ -32,23 +38,48 @@ execution-order = "axis-1 > axis-2 > axis-3" [axis-1-scoping] required = true -sources = "README, roadmap, status docs, maintenance checklist, CI/security docs" -markers = "TODO/FIXME/XXX/HACK/STUB/PARTIAL" -idris-unsound-markers = "believe_me/assert_total" -output = "scoped work assembly in must/intend/like buckets" +sources = "README, roadmap, readiness/proof-needs, package manifests and READMEs, CI/security policy, and current source" +markers = ["TODO", "FIXME", "XXX", "HACK", "STUB", "PARTIAL"] +idris-unsound-markers = ["believe_me", "assert_total"] +output = "scoped-work-assembly under must/intend/like with explicit evidence and skips" [axis-2-maintenance] corrective-first = true +corrective = "Fix defects, regressions, broken checks, and safety/security issues." adaptive-second = true -adaptive-focus = "scope changes, stale references, obsolete work culling" +adaptive-focus = "Reconcile stale claims and paths; remove obsolete work; align source and docs." perfective-third = true -perfective-source = "honest state from axis-1 after corrective/adaptive updates" +perfective-source = "Only after the current honest state is established." [axis-3-audit] systems-check = true -compliance-check = true -effects-check = true -compliance-focus = "seams/compromises/exception register and anti-drift" -compliance-tooling = "panic-attack" -effects-tooling = "ecological checking with sustainabot guidance" -effects-evidence = "benchmark evidence and maintainer dialogue/status review" +documentation-honesty-check = true +safety-security-accounted-check = true +compliance-seams-check = true +effects-review-check = true +benchmark-evidence-required-when-performance-is-claimed = true +maintainer-dialogue-review-required = true +compliance-tooling-baseline = "panic-attack; optional external tool, must report unavailable rather than silently pass" +effects-tooling-baseline = "sustainabot-guided ecological checking when applicable; not run or claimed without evidence" + +[generic-cleanup-finish-off] +root-cleanup-required = true +stale-work-cull-required = true +docs-parity-required = true +machine-human-sync-required = true +release-prep-summary-required = true +next-actions-required = ["corrective", "adaptive", "perfective"] + +[repo-specific-controls] +root-toolchain-config = "mise.toml" +root-task-runner = "Justfile" +security-policy = "SECURITY.adoc" +security-contact = ".well-known/security.txt" +maintainer-list = "MAINTAINERS" +source-smoke-command = "just test-static" +compiler-backed-command = "just test (requires Idris2 and Zig)" +container-deployment = "disabled until an executable service and runtime tests exist" + +[maintenance-status] +latest-assessment = "2026-09-27" +current-compiler-verification = "not run in the assessment workspace; see READINESS.adoc" diff --git a/.machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml b/.machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml index fef9ca02..b6f3da06 100644 --- a/.machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml +++ b/.machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml @@ -1,17 +1,17 @@ # SPDX-License-Identifier: MPL-2.0 -# Cross-repo maintenance baseline (machine-readable canonical) +# Cross-repo maintenance baseline, reconciled with proven-servers paths. [metadata] -version = "1.1.0" -last-updated = "2026-02-24" -scope = "cross-repo" -source-human = "docs/maintenance/MAINTENANCE-CHECKLIST.md" +version = "1.2.0" +last-updated = "2026-09-27" +scope = "estate baseline; repo-specific applicability recorded below" +source-human = "docs/maintenance/MAINTENANCE-CHECKLIST.adoc" companion-human = "docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc" companion-machine = ".machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml" [policy] single-source = true -notes = "Use this file as canonical machine policy and keep markdown synchronized." +notes = "This repository-specific checklist is canonical for applied paths and status; conditional template features are not silently treated as present." [maintenance-axes] scoping-first = true @@ -21,139 +21,92 @@ axis-2 = "corrective > adaptive > perfective" axis-3 = "systems > compliance > effects" [scoping] -inputs_required = [ - "README", - "roadmap", - "status-docs", - "maintenance-checklist", - "ci-and-security-docs", +inputs-required = [ + "README.adoc", + "ROADMAP.adoc", + "READINESS.adoc", + "PROOF-NEEDS.adoc", + "package manifests and package READMEs", + "CI and security documentation", ] - -marker_scan_required = [ - "TODO", - "FIXME", - "XXX", - "HACK", - "STUB", - "PARTIAL", -] - -idris_unsound_scan_required = [ - "believe_me", - "assert_total", -] - -scope_assembly_buckets = ["must", "intend", "like"] - -[axis-2-maintenance-rules] -corrective-first = true -adaptive-second = true -adaptive_examples = [ - "scope-change reconciliation", - "stale-reference removal", - "obsolete-work culling", -] -perfective-third = true -perfective_source = "axis-1 honest state after corrective/adaptive updates" +marker-scan-required = ["TODO", "FIXME", "XXX", "HACK", "STUB", "PARTIAL"] +idris-unsound-scan-required = ["believe_me", "assert_total"] +output = "scoped worklist with must/intend/like priorities and explicit evidence gaps" [axis-3-audit-rules] systems-check = true documentation-honesty-check = true safety-security-accounted-check = true effects-review-check = true -benchmark-evidence-required = true +benchmark-evidence-required-when-performance-is-claimed = true maintainer-dialogue-review-required = true -compliance-seams-check = true -exception-register-required = true -exception-bounded-scope-required = true -policy-drift-contamination-check = true -example-drift-risk = "single TypeScript exception causing broad ReScript->TypeScript migration" -compliance-tooling = "panic-attack" -effects-tooling = "ecological checking with sustainabot guidance" +compliance-tooling = "panic-attack if installed; missing tool is an explicit skip/blocker" +effects-tooling = "sustainabot guidance only when applicable and actually consulted" -[generic-cleanup-finish-off] +[cleanup-finish-off] root-cleanup-required = true stale-work-cull-required = true docs-parity-required = true machine-human-sync-required = true -compliance-finish-off-required = true -effects-finish-off-required = true release-prep-summary-required = true next-actions-required = ["corrective", "adaptive", "perfective"] [must] -root_control_files = [ +root-control-files = [ ".gitignore", ".gitattributes", ".editorconfig", - ".tool-versions", - "Containerfile", + "mise.toml", "Justfile", ] - -root_hosting_files = [ - "CNAME", - ".nojekyll", -] - -ownership_files = [ - "MAINTAINER", - ".github/CODEOWNERS", -] - -machine_readable_required = [ - ".machine_readable/anchors/ANCHOR.a2ml", +root-hosting-files = [] +root-hosting-note = "Add .nojekyll/CNAME only when an actual Pages site/custom domain is configured." +custom-domain-file = "CNAME only when a custom domain is configured" +ownership-files = ["MAINTAINERS", ".github/CODEOWNERS"] +machine-readable-required = [ + ".machine_readable/6a2/", + ".machine_readable/6a2/anchor/ANCHOR.a2ml", ".machine_readable/contractiles/", ".machine_readable/ai/", ".machine_readable/bot_directives/", ] - -contractiles_required = [ - "Mustfile", - "Trustfile", - "Intentfile", +contractiles-required = [ + ".machine_readable/contractiles/Mustfile.a2ml", + ".machine_readable/contractiles/Trustfile.a2ml", + ".machine_readable/contractiles/Intentfile.a2ml", + ".machine_readable/contractiles/Adjustfile.a2ml", ] - -security_required = [ +security-required = [ + "SECURITY.adoc", ".well-known/security.txt", - "ci-security-scan", + "GitHub Actions security scanning (coverage must be reviewed)", ] -quality_gate_required = [ - "format", - "lint", - "unit-tests", - "integration-tests", - "p2p-tests", - "e2e-tests", - "bench-smoke", - "docs-check", - "security-scan", +[quality-gate-baseline] +expected-areas = ["format", "lint", "unit-tests", "integration-tests", "p2p-tests", "e2e-tests", "bench-smoke", "docs-check", "security-scan"] +repo-configured = ["Git whitespace check", "shell syntax and binding policy", "static source smoke", "package-specific Idris2 and Zig tasks"] +repo-not-configured-or-not-established = [ + "repository-wide language formatter", + "full multi-language lint matrix", + "complete unit/integration/p2p/e2e test matrix", + "benchmark smoke evidence", + "repository-wide documentation build/check", + "complete binding link/runtime matrix", ] -abi_ffi_policy = [ - "ABI Idris2 in src/abi/*.idr", - "FFI Zig in ffi/**/*.zig", -] +[abi-ffi-policy] +applicability = "Package-specific; do not infer that every package has both sides or that the bridge is verified." +model-language = "Idris2 where used" +ffi-language = "Zig where used" +required-evidence = "Build and execute boundary/conformance tests for each claimed interface." [should] -docs_primary_format = "adoc" -docs_structure = [ - "docs/theory", - "docs/practice", - "docs/whitepapers/academic", - "docs/whitepapers/industry", - "docs/proofs", - "docs/reports", -] - -root_minimization = true -well_known_metadata = true -roadmap_honesty_with_dates = true -ci_doc_format_policy = true +docs-primary-format = "adoc, except ecosystem-required tool files" +well-known-metadata = true +roadmap-status-evidence-dated = true +ci-doc-format-policy = "No repository-wide format check is configured." [could] -generate_human_from_machine = true -mode_aware_bots = ["corrective", "adaptive", "perfective", "audit"] -topology_dashboard = true -exception_registry = true +generate-human-from-machine = true +mode-aware-bots = ["corrective", "adaptive", "perfective", "audit"] +exception-registry = true diff --git a/.machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml b/.machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml index 093573a0..cb61b03b 100644 --- a/.machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml +++ b/.machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml @@ -1,10 +1,10 @@ # SPDX-License-Identifier: MPL-2.0 -# General software development approach (machine-readable) +# General software development approach for proven-servers. [metadata] -version = "1.0.0" -last-updated = "2026-02-24" -scope = "cross-repo" +version = "1.1.0" +last-updated = "2026-09-27" +scope = "repo" source-human = "docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc" [execution] @@ -14,40 +14,47 @@ order = ["axis-1", "axis-2", "axis-3"] name = "scope" priority = "must > intend > like" inputs = [ - "README", - "roadmap", - "status-docs", - "ci-and-security-docs", + "README.adoc", + "ROADMAP.adoc", + "READINESS.adoc", + "PROOF-NEEDS.adoc", + "package manifests and READMEs", + "CI/security documentation", + "current source and tests", ] marker-scan = ["TODO", "FIXME", "XXX", "HACK", "STUB", "PARTIAL"] idris-unsound-scan = ["believe_me", "assert_total"] -output = "scoped-work-assembly" +output = "scoped work assembly with evidence gaps and explicit skips" [axis-2] name = "maintenance" priority = "corrective > adaptive > perfective" -corrective = "defect/regression/safety/security fixes" -adaptive = "scope reconciliation, stale-reference removal, obsolete-work culling" -perfective = "quality improvements derived from axis-1 honest state" +corrective = "Fix concrete defects, regressions, broken checks, unsafe security paths, and unsupported claims." +adaptive = "Reconcile changed scope, stale paths, obsolete tasks, and human/machine metadata." +perfective = "Improve quality only after the current honest state is established." [axis-3] name = "audit" priority = "systems > compliance > effects" -systems = "required systems present and operating" -compliance = "exceptions explicit, bounded, and drift-resistant" -effects = "benchmark/operational impact evidence captured and reviewed" -compliance-tooling = "panic-attack" -effects-tooling = "ecological checking with sustainabot guidance" +systems = "Check that configured mechanisms exist and operate." +compliance = "Record applicable requirements, scoped exceptions, and anti-drift evidence." +effects = "Capture benchmark/operational impact evidence only when effects are claimed." +compliance-tooling = "panic-attack where applicable; report missing tool explicitly" +effects-tooling = "sustainabot-guided ecological checking where applicable and actually run" [cleanup-finish-off] root-cleanup = true stale-work-cull = true docs-sync-human-machine = true compliance-audit = true -effects-audit = true -release-summary = ["must", "should", "could"] +effects-audit-when-claimed = true +release-summary = ["must", "intend", "like"] next-actions = ["corrective", "adaptive", "perfective"] [collaboration] maintainer-dialogue-required = true -dialogue-topics = ["what changed", "why", "remaining risks"] +dialogue-topics = ["what changed", "why", "remaining risks", "next actions"] + +[evidence-boundary] +source-smoke-tests = "Heuristics only; not formal proofs, exhaustive tests, or ABI conformance." +historical-reports = "Revision-specific; not current verification." diff --git a/.machine_readable/rsr-profile.a2ml b/.machine_readable/rsr-profile.a2ml new file mode 100644 index 00000000..2a8a4432 --- /dev/null +++ b/.machine_readable/rsr-profile.a2ml @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: MPL-2.0 +# RSR capability inventory for this project-specific source monorepo. +# The external template-applicability policy is DRAFT; this profile is not a +# conformance certificate and deliberately avoids template-only capabilities. + +[profile] +capabilities = ["bash", "idris2", "zig"] + +[rationale] +bash = "Maintained non-trivial shell tooling exists under tests/, tools/, and the repository root." +idris2 = "Maintained Idris2 package source exists under protocols/, core/, and connectors/." +zig = "Maintained Zig package source and build manifests exist under protocols/, core/, connectors/, and ffi/." +no-ffi = "Per-package FFI source exists, but the standards draft's gated src/interface/ffi/ module and repository-wide interop evidence are not present." +no-abi = "No repository-wide formally specified ABI with generated-header correspondence is established." +no-formal-proofs = "There is no verification/proofs/ module meeting the standards draft's gate; package proof declarations are not inferred as this capability." +no-api-service = "Experimental server source is not a validated, deployable network service; the former full-E2E workflow path has been removed." +no-container = "No container image is shipped. Disabled container/deployment scaffolding has been removed from the root package shape." +no-docs-site = "No project-specific static site is maintained; the competing placeholder Pages workflows have been removed." +no-published-package = "No validated registry package or release artifact is published; release automation has been removed." +no-reproducible-build = "No reproducible Guix/Nix build is configured." +no-benchmarks = "No benchmark suite exists; the empty benches placeholder has been removed." +no-governance-tier = "This repository does not carry the standards draft's complete AUDIT/AFFIRMATION/GOVERNANCE/MAINTAINERS module set." diff --git a/.mise.toml b/.mise.toml deleted file mode 100644 index d033e515..00000000 --- a/.mise.toml +++ /dev/null @@ -1,2 +0,0 @@ -[tools] -just = "1.36.0" diff --git a/.nojekyll b/.nojekyll deleted file mode 100644 index e69de29b..00000000 diff --git a/.well-known/security.txt b/.well-known/security.txt new file mode 100644 index 00000000..0af1ab99 --- /dev/null +++ b/.well-known/security.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: MPL-2.0 +# RFC 9116 security contact for proven-servers + +Contact: mailto:j.d.a.jewell@open.ac.uk +Expires: 2027-09-27T23:59:59.000Z +Preferred-Languages: en +Canonical: https://github.com/hyperpolymath/proven-servers/blob/main/.well-known/security.txt +Policy: https://github.com/hyperpolymath/proven-servers/blob/main/SECURITY.adoc diff --git a/.windsurfrules b/.windsurfrules index 112dca49..bcdbce3d 100644 --- a/.windsurfrules +++ b/.windsurfrules @@ -1,43 +1,20 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# Authoritative source: docs/AI-CONVENTIONS.md +# Authoritative guidance: docs/AI-CONVENTIONS.adoc -# STARTUP: Read 0-AI-MANIFEST.a2ml first, then .machine_readable/STATE.a2ml. +# STARTUP: Read 0-AI-MANIFEST.a2ml, .machine_readable/6a2/STATE.a2ml, +# and .machine_readable/6a2/anchor/ANCHOR.a2ml. -# LICENSE -# All original code: MPL-2.0. -# Never AGPL-3.0. MPL-2.0 only as platform-required fallback. -# SPDX header required on every source file. -# Copyright: Jonathan D.A. Jewell (hyperpolymath) +# Distinguish model proofs, native tests, ABI correspondence, binding linking, +# and protocol interoperability. Report skipped tools and checks explicitly. +# Never treat source-pattern smoke checks or historical audit reports as +# current runtime/proof evidence. -# STATE FILES (.machine_readable/ ONLY) -# Never create in repo root: STATE.a2ml, META.a2ml, ECOSYSTEM.a2ml, -# AGENTIC.a2ml, NEUROSYM.a2ml, PLAYBOOK.a2ml. -# The .machine_readable/ directory is the single source of truth. +# Keep operations fail-closed when credentials, key material, or a verified +# backend are absent. Container deployment is disabled in the current tree. -# BANNED PATTERNS -# Idris2: believe_me, assert_total, assert_smaller, unsafePerformIO -# Haskell: unsafeCoerce, unsafePerformIO, undefined, error -# OCaml: Obj.magic, Obj.repr, Obj.obj -# Coq: Admitted -# Lean: sorry -# Rust: transmute (unless FFI with // SAFETY: comment) +# Existing binding languages are allowed as source inventory. Do not add a new +# language/runtime without a documented scope and toolchain rationale. -# BANNED LANGUAGES -# TypeScript -> ReScript -# Node.js / npm / bun -> Deno -# Go -> Rust -# Python -> Julia or Rust - -# CONTAINERS -# Runtime: Podman (never Docker). -# File: Containerfile (never Dockerfile). -# Base: cgr.dev/chainguard/wolfi-base:latest or cgr.dev/chainguard/static:latest. - -# ABI/FFI -# ABI: Idris2 with dependent types (src/abi/). -# FFI: Zig with C ABI (ffi/zig/). -# Headers: generated/abi/. - -# BUILD: Use just (justfile) for all tasks. -# STYLE: Descriptive names. Document all files. SPDX headers everywhere. +# Preserve SPDX and third-party license notices. Original project code is +# generally MPL-2.0. See docs/AI-CONVENTIONS.adoc for configured tasks. diff --git a/0-AI-MANIFEST.a2ml b/0-AI-MANIFEST.a2ml index 0e4df5b2..80c3d8cd 100644 --- a/0-AI-MANIFEST.a2ml +++ b/0-AI-MANIFEST.a2ml @@ -1,195 +1,69 @@ -# ⚠️ STOP - CRITICAL READING REQUIRED - -**THIS FILE MUST BE READ FIRST BY ALL AI AGENTS** - -## WHAT IS THIS? - -This is the AI manifest for **[YOUR-REPO-NAME]**. It declares: -- Canonical file locations (where things MUST be, and nowhere else) -- Critical invariants (rules that must NEVER be violated) -- Repository structure and organization - -## CANONICAL LOCATIONS (UNIVERSAL RULE) - -### Machine-Readable Metadata: `.machine_readable/` ONLY - -These 6 a2ml files MUST exist in `.machine_readable/` directory ONLY: -1. **STATE.a2ml** - Project state, progress, blockers -2. **META.a2ml** - Architecture decisions, governance -3. **ECOSYSTEM.a2ml** - Position in ecosystem, relationships -4. **AGENTIC.a2ml** - AI agent interaction patterns -5. **NEUROSYM.a2ml** - Neurosymbolic integration config -6. **PLAYBOOK.a2ml** - Operational runbook - -**CRITICAL:** If ANY of these files exist in the root directory, this is an ERROR. - -### Anchor File: `.machine_readable/anchors/ANCHOR.a2ml` ONLY - -Canonical authority and semantic-boundary declaration MUST exist at: - -` .machine_readable/anchors/ANCHOR.a2ml ` - -Do not place `ANCHOR.a2ml` at repository root. - -### Maintenance Policies: `.machine_readable/policies/` ONLY - -Canonical maintenance/governance files MUST exist under: - -` .machine_readable/policies/ ` - -Minimum required files: -- `MAINTENANCE-AXES.a2ml` -- `MAINTENANCE-CHECKLIST.a2ml` -- `SOFTWARE-DEVELOPMENT-APPROACH.a2ml` - -Do not place maintenance policy files in repository root. - -### Bot Directives: `.machine_readable/bot_directives/` ONLY - -Bot-specific instructions for your automated agents. Example roles: -- git-ops-bot - Git operations -- quality-bot - Code quality -- deps-bot - Dependency updates -- docs-bot - Documentation -- integration-bot - Integration -- completion-bot - Task completion - -### Contractiles: `.machine_readable/contractiles/` ONLY - -Policy enforcement contracts: -- k9 - Operational constraints (Nickel) -- dust - Rollback and recovery semantics -- lust - Future intent and roadmap direction -- must - Required invariants and validations -- trust - Cryptographic verification (Haskell) - -### Agent Instructions - -- `.claude/CLAUDE.md` - Claude-specific patterns (if exists) -- `0-AI-MANIFEST.a2ml` - THIS FILE (universal entry point) - -## CORE INVARIANTS - -1. **No state file duplication** - Root must NOT contain STATE.a2ml, META.a2ml, etc. -2. **Single source of truth** - `.machine_readable/` is authoritative -3. **No stale metadata** - If root state files exist, they are OUT OF DATE -4. **License consistency** - All code MPL-2.0 unless platform requires MPL-2.0 -5. **Author attribution** - Always "Jonathan D.A. Jewell " -6. **Container images** - MUST use Chainguard base (`cgr.dev/chainguard/wolfi-base:latest` or `cgr.dev/chainguard/static:latest`) -7. **Container runtime** - Podman, never Docker. Files are `Containerfile`, never `Dockerfile` -8. **Container orchestration** - `selur-compose`, never `docker-compose` - -## REPOSITORY STRUCTURE - - - -This repo contains: - -``` -[YOUR-REPO-NAME]/ -├── 0-AI-MANIFEST.a2ml # THIS FILE (start here) -├── README.md # Project overview -├── [your source files] # Main code -├── container/ # Stapeln container ecosystem templates -│ ├── compose.toml # selur-compose orchestration -│ ├── compose.example.toml # Concrete multi-service example -│ ├── Containerfile # Multi-stage OCI build -│ ├── manifest.toml # Cerro-torre bundle metadata -│ ├── .gatekeeper.yaml # Svalinn edge gateway policy -│ ├── ct-build.sh # Build/sign/verify pipeline -│ ├── entrypoint.sh # Container entrypoint -│ ├── vordr.toml # Runtime monitoring -│ ├── deploy.k9.ncl # k9-svc Hunt-level deployment -│ ├── 0-AI-MANIFEST.a2ml # AI manifest for container dir -│ └── README.adoc # Container directory documentation -└── .machine_readable/ # ALL machine-readable content - ├── STATE.a2ml # Project state, progress, blockers - ├── META.a2ml # Architecture decisions, governance - ├── ECOSYSTEM.a2ml # Ecosystem position, relationships - ├── AGENTIC.a2ml # AI agent interaction patterns - ├── NEUROSYM.a2ml # Neurosymbolic integration config - ├── PLAYBOOK.a2ml # Operational runbook - ├── anchors/ # Canonical authority declarations - │ └── ANCHOR.a2ml # Upstream anchor and policy boundary - ├── policies/ # Canonical governance policies - │ ├── MAINTENANCE-AXES.a2ml - │ ├── MAINTENANCE-CHECKLIST.a2ml - │ └── SOFTWARE-DEVELOPMENT-APPROACH.a2ml - ├── bot_directives/ # Per-bot rules and constraints - └── contractiles/ # Policy enforcement contracts (k9, dust, lust, must, trust) -``` - -## SESSION STARTUP CHECKLIST - -✅ Read THIS file (0-AI-MANIFEST.a2ml) first -✅ Understand canonical location: `.machine_readable/` (state, anchors, bots, contractiles) -✅ Know the invariants (no state file duplication, etc.) -✅ Check for MCP enforcement (if applicable) -✅ Read `.machine_readable/policies/MAINTENANCE-AXES.a2ml` for axis ordering and audit expectations -✅ Read `.machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml` for maintenance baseline controls -✅ Read `.machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml` for execution sequence -✅ Read `.machine_readable/6a2/STATE.a2ml` for current status -✅ Read `.machine_readable/6a2/AGENTIC.a2ml` for interaction patterns - -## LIFECYCLE HOOKS - -### on-enter (Session Start) - -When starting a new session: - -1. Read and acknowledge this manifest -2. Log session start (optional but recommended) - - Format: `[YYYY-MM-DD HH:MM:SS] Session started: [agent-name]` - - Location: `.machine_readable/session-log.txt` -3. Read `.machine_readable/6a2/STATE.a2ml` -4. Check for blockers -5. State understanding of canonical locations - -### on-exit (Session End) - -When ending a session: - -1. Update `.machine_readable/6a2/STATE.a2ml` if changes made -2. Log session end (optional but recommended) - - Format: `[YYYY-MM-DD HH:MM:SS] Session ended: [summary]` - - Location: `.machine_readable/session-log.txt` -3. Document new blockers -4. Summarize outcomes - -## Calling ECHIDNA via BoJ - -All ECHIDNA invocations go through the `echidna-llm-mcp` BoJ cartridge. -**Never call ECHIDNA directly — always go through BoJ.** - -| Tool | Description | -|------|-------------| -| `echidna_list_provers` | Discover all 105 provers with tier/category/complexity | -| `echidna_prove` | Invoke a prover on proof content (typed outcome) | -| `echidna_verify` | Verify inline proof content (typed outcome) | -| `echidna_verify_raw` | Direct binary invocation (EProver, CaDiCaL, SAT solvers) | -| `echidna_suggest` | Neural tactic suggestions (Julia ML corpus-backed) | -| `echidna_suggest_tactics` | Aspect-tag tactic suggestions — advisory only | -| `echidna_search` | Keyword search over 66,674-proof corpus | -| `echidna_session_create` | Start interactive tactic session | - -```json -{ "tool": "echidna_list_provers", "args": {} } -{ "tool": "echidna_prove", "args": { "prover": "Lean", "content": "theorem t : 1 + 1 = 2 := rfl" } } -{ "tool": "echidna_suggest_tactics", "args": { "goal": "n + 0 = n", "prover": "Lean" } } -``` - -Full protocol: `boj-server/cartridges/echidna-llm-mcp/docs/CALL-PROTOCOL.adoc` - -## ATTESTATION PROOF - -After reading this file, demonstrate understanding by stating: - -**"I have read the AI manifest. All machine-readable content (state files, anchors, policies, bot directives, contractiles) is located in `.machine_readable/` ONLY, and I will not create duplicate files in the root directory."** - -## META - -- **Format Version:** 1.0.0 -- **Created:** [DATE] -- **Maintained By:** [YOUR-NAME/ORG] -- **License:** MPL-2.0 -- **Protocol:** https://github.com/hyperpolymath/0-ai-gatekeeper-protocol +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# AI/automation entry point for the proven-servers source monorepo. + +## Repository scope + +`proven-servers` is a research/prototype source monorepo containing protocol +models, Idris2 packages, Zig FFI prototypes, binding scaffolds, tests, and +documentation. It is not a distribution of production-ready network servers. +Directory names, source comments, generated-looking headers, inventories, and +historical audits do not establish executable support, protocol conformance, +ABI equivalence, security assurance, or deployment readiness. + +## Canonical project metadata + +* Core state and guidance: `.machine_readable/6a2/`. +* Canonical anchor: `.machine_readable/6a2/anchor/ANCHOR.a2ml`. +* Maintenance policies: `.machine_readable/policies/`. +* Bot directives: `.machine_readable/bot_directives/`. +* Contractile policy/intent: `.machine_readable/contractiles/`. +* Repository-specific task wrapper: root `contractile.just`, imported optionally + by the root `Justfile`. +* Conservative RSR capability inventory: `.machine_readable/rsr-profile.a2ml`. + +Do not create duplicate core state documents at the repository root. The +profile and local `validate-rsr` task are inventories/checks, not an RSR +certification; the external capability applicability policy is a draft. + +## Evidence and claims + +1. Read `README.adoc`, `READINESS.adoc`, `PROOF-NEEDS.adoc`, + `.machine_readable/6a2/STATE.a2ml`, and the package-specific README before + making implementation or readiness claims. +2. Record exact commit, toolchain versions, commands, and results for checks. +3. Distinguish compiler builds, executable tests, source-pattern smoke checks, + formal proof, ABI conformance, protocol conformance, and deployment tests. +4. An Idris2 type/proof declaration is not compiler-checked evidence until the + declared package builds. It does not prove independent Zig/C/binding parity. +5. Binding directories and historical reports are not support/build evidence. + Follow `.machine_readable/BINDINGS.a2ml` and `READINESS.adoc`. +6. Do not claim a security key, release artifact, benchmark result, service, + endpoint, external integration, or runtime capability that has not been + verified from the current checkout. + +## Safe task execution + +* `Justfile` is the task entry point. Inspect recipe bodies before execution. +* Build/test recipes may invoke local compilers and write build artifacts; they + fail if their required tools are missing. +* Static smoke checks are explicitly heuristic and are not proofs or + conformance tests. +* `setup.sh` is informational and non-mutating. It does not install packages. +* There is no `just init`, `just setup`, `just install`, or automated rollback. +* `just doctor` only reports local tool availability and Git state; it does not + repair or install anything. `just assail` invokes an optional external scan. +* Root container/cloud deployment, package release, and Pages publishing are + not supported deliverables. Do not introduce publishing or cloud side + effects without an explicitly reviewed artifact and authorization. +* Never run broad placeholder substitutions, firewall/SELinux changes, or + downloaded scripts piped to a shell as repository setup. +* Do not commit, push, create a release, deploy, or alter credentials unless + explicitly requested by the user and permitted by the session environment. + +## Session handoff + +Update `.machine_readable/6a2/STATE.a2ml` when substantive verification status +changes. Do not create ad-hoc provenance or session logs; use a repository tool +only when its format and validation contract are defined. diff --git a/ABI-FFI-README.adoc b/ABI-FFI-README.adoc index 46e5c562..a1164f44 100644 --- a/ABI-FFI-README.adoc +++ b/ABI-FFI-README.adoc @@ -1,341 +1,82 @@ -== proven-servers ABI/FFI Documentation - -=== Overview - -This library follows the *Hyperpolymath RSR Standard* for ABI and FFI -design: - -* *ABI (Application Binary Interface)* defined in *Idris2* with formal -proofs -* *FFI (Foreign Function Interface)* implemented in *Zig* for C -compatibility -* *Generated C headers* bridge Idris2 ABI to Zig FFI -* *Any language* can call through standard C ABI - -=== Architecture - -.... -┌─────────────────────────────────────────────┐ -│ ABI Definitions (Idris2) │ -│ src/ConnABI/ │ -│ - Layout.idr (Tag encodings + proofs) │ -│ - Transitions.idr (State machine GADTs) │ -│ - Foreign.idr (Opaque handles + FFI) │ -└─────────────────┬───────────────────────────┘ - │ - │ generates - ▼ -┌─────────────────────────────────────────────┐ -│ C Headers (generated) │ -│ generated/abi/.h │ -└─────────────────┬───────────────────────────┘ - │ - │ imported by - ▼ -┌─────────────────────────────────────────────┐ -│ FFI Implementation (Zig) │ -│ ffi/zig/src/.zig │ -│ - Implements C-compatible functions │ -│ - Zero-cost abstractions │ -│ - Memory-safe by default │ -└─────────────────┬───────────────────────────┘ - │ - │ compiled to libproven_.so/.a - ▼ -┌─────────────────────────────────────────────┐ -│ Any Language via C ABI │ -│ - Rust, ReScript, Gleam, Elixir, etc. │ -└─────────────────────────────────────────────┘ -.... - -=== Directory Structure - -Each connector follows this layout: - -.... -connectors/proven-/ -├── src/ -│ ├── Conn.idr # Core types and constants -│ ├── Conn/Types.idr # Sum type definitions -│ ├── Conn/Main.idr # Entry point -│ ├── ConnABI.idr # Re-export module -│ └── ConnABI/ -│ ├── Layout.idr # Tag encodings + roundtrip proofs -│ ├── Transitions.idr # GADT state machine + witnesses -│ └── Foreign.idr # Opaque handles + FFI contract -├── generated/abi/ -│ └── .h # C ABI header -└── ffi/zig/ - ├── build.zig # Build configuration - ├── src/.zig # C-compatible FFI implementation - └── test/_test.zig # Integration tests -.... - -=== Why Idris2 for ABI? - -==== 1. *Formal Verification* - -Idris2’s dependent types prove properties about the ABI at compile-time: - -[source,idris] ----- --- Prove tag encoding roundtrips correctly -tagToStorageOpRoundtrip : (x : StorageOp) -> tagToStorageOp (storageOpToTag x) = Just x -tagToStorageOpRoundtrip PutObject = Refl -tagToStorageOpRoundtrip GetObject = Refl -tagToStorageOpRoundtrip DeleteObject = Refl --- ...every variant reduces to Refl ----- - -==== 2. *State Machine Proofs* - -Encode exactly which transitions are legal as a GADT: - -[source,idris] ----- -data ValidTransition : AuthState -> AuthState -> Type where - InitAuth : ValidTransition Unauthenticated Challenging - DirectAuth : ValidTransition Unauthenticated Authenticated - LockOut : ValidTransition Unauthenticated Locked - -- Invalid transitions are simply absent — impossible to construct ----- - -==== 3. *Capability Witnesses* - -Prove at the type level which states permit which operations: - -[source,idris] ----- -data CanAuthenticate : AuthState -> Type where - AuthWhenUnauth : CanAuthenticate Unauthenticated - --- Impossible in other states — the compiler proves this -noAuthFromLocked : CanAuthenticate Locked -> Void -noAuthFromLocked x impossible ----- - -==== 4. *Decidability* - -Compile-time branching on capability: - -[source,idris] ----- -decCanAuthenticate : (s : AuthState) -> Dec (CanAuthenticate s) -decCanAuthenticate Unauthenticated = Yes AuthWhenUnauth -decCanAuthenticate _ = No (\case AuthWhenUnauth impossible) ----- - -=== Why Zig for FFI? - -==== 1. *C ABI Compatibility* - -Zig exports C-compatible functions naturally: - -[source,zig] ----- -pub export fn authconn_abi_version() callconv(.c) u32 { - return ABI_VERSION; -} ----- - -==== 2. *Memory Safety* - -Compile-time safety without runtime overhead: - -[source,zig] ----- -const handle = h orelse return AuthError.invalid_handle; -// NULL check enforced — impossible to dereference null ----- - -==== 3. *Cross-Compilation* - -Built-in cross-compilation to any platform: - -[source,bash] ----- -zig build -Dtarget=x86_64-linux -zig build -Dtarget=aarch64-macos -zig build -Dtarget=x86_64-windows ----- - -==== 4. *Zero Dependencies* - -No runtime, no libc required (unless explicitly needed). - -=== Components with ABI-FFI (as of 2026-03-01) - -[cols=",,,,",options="header",] -|=== -|Component |States |Transitions |Tests |Library -|proven-dbconn |5 |9 |✓ |libproven_dbconn -|proven-authconn |6 |11 |20 |libproven_authconn -|proven-cacheconn |4 |8 |13 |libproven_cacheconn -|proven-queueconn |5 |11 |17 |libproven_queueconn -|proven-resolverconn |4 |9 |12 |libproven_resolverconn -|proven-storageconn |5 |11 |14 |libproven_storageconn -|=== - -=== Building - -==== Build a single connector - -[source,bash] ----- -cd connectors/proven-dbconn/ffi/zig -zig build # Build debug (shared + static) -zig build -Doptimize=ReleaseFast # Build optimised -zig build test # Run integration tests ----- - -==== Build all connectors - -[source,bash] ----- -for conn in dbconn authconn cacheconn queueconn resolverconn storageconn; do - (cd connectors/proven-$conn/ffi/zig && zig build test) && echo "$conn: OK" -done ----- - -==== Cross-Compile - -[source,bash] ----- -cd connectors/proven-dbconn/ffi/zig -zig build -Dtarget=x86_64-linux -zig build -Dtarget=aarch64-macos -zig build -Dtarget=x86_64-windows ----- - -=== Usage - -==== From C - -[source,c] ----- -#include "dbconn.h" - -int main() { - dbconn_error_t err; - dbconn_handle_t *h = dbconn_connect("localhost", 5432, 1, &err); - if (!h || err != DBCONN_ERR_NONE) return 1; - - // State is now Connected — can query - err = dbconn_query(h, "SELECT 1", 8, NULL, 0, NULL); - - dbconn_disconnect(h); - return 0; -} ----- - -Compile with: - -[source,bash] ----- -gcc -o example example.c -lproven_dbconn -L./zig-out/lib ----- - -==== From Rust - -[source,rust] ----- -#[link(name = "proven_authconn")] -extern "C" { - fn authconn_abi_version() -> u32; - fn authconn_create_session(method: u8, err: *mut u8) -> *mut std::ffi::c_void; - fn authconn_authenticate(h: *mut std::ffi::c_void, - cred: *const u8, cred_len: u32) -> u8; - fn authconn_destroy_session(h: *mut std::ffi::c_void) -> u8; -} - -fn main() { - unsafe { - assert_eq!(authconn_abi_version(), 1); - let mut err: u8 = 0; - let h = authconn_create_session(0, &mut err); // password auth - assert!(!h.is_null()); - authconn_destroy_session(h); - } -} ----- - -==== From Julia - -[source,julia] ----- -const libdbconn = "libproven_dbconn" - -function connect(host, port, require_tls) - err = Ref{UInt8}(0) - h = ccall((:dbconn_connect, libdbconn), Ptr{Cvoid}, - (Cstring, UInt16, UInt8, Ptr{UInt8}), - host, port, require_tls, err) - h == C_NULL && error("Connection failed: error $(err[])") - h -end - -function disconnect(h) - ccall((:dbconn_disconnect, libdbconn), UInt8, (Ptr{Cvoid},), h) -end - -h = connect("localhost", 5432, 1) -try - # ... use connection ... -finally - disconnect(h) -end ----- - -=== Testing - -==== Run connector tests +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += ABI/FFI Status and Verification Boundary + +:revdate: 2026-09-27 +:toc: +:toclevels: 2 + +[IMPORTANT] +==== +There is no verified, repository-wide ABI/FFI bridge covering all components. +The source tree contains package-specific Idris2 models, Zig C-ABI exports, +generated artifacts, and language-binding sources with varying coverage. A +matching filename or enum table is not evidence that the compiled interfaces +match or that a binding links successfully. +==== + +== What the repository contains + +* Idris2 model and ABI modules live at the repository root and inside individual + protocol, core, and connector packages. Package `.ipkg` manifests determine + which modules are actually compiled. +* Zig FFI implementations and `build.zig` files are package-specific. Some + packages export a C calling convention; implementation and test coverage vary. +* Generated C headers and Zig ABI artifacts are present only for selected + packages. `tools/gen-abi.sh` applies to packages configured for that generator; + it is not a universal proof of all package interfaces. +* `bindings/` contains 20 language-named directories. It is an inventory, not a + claim that 20 language bindings build, link, or are supported. + +== Proof boundary + +An Idris2 type-check proves the properties stated by the compiled model, subject +to the compiler, imports, and totality settings of that build. It does not prove +that a separately maintained Zig FFI, generated header, or language wrapper +implements the model. That requires explicit generated-interface checks and +executed tests against the compiled library. + +A C ABI declaration alone is not an implementation. In particular, raw Zig C +symbols cannot be declared as OCaml `external` primitives: OCaml's primitive +calling convention requires compatible C stubs. The OCaml native calls now +raise an explicit unavailable error until such stubs exist. See +`bindings/ocaml/README.adoc`. + +== Package-level verification + +Use the manifest and README inside the component being examined. Typical local +commands are: [source,bash] ---- -cd connectors/proven-storageconn/ffi/zig -zig build test ----- - -Tests cover: - *ABI version* — `+_abi_version()+` returns 1 - -*Lifecycle* — connect → operate → disconnect - *Invalid transitions* — -wrong-state operations return errors - *NULL safety* — all functions -handle NULL handles gracefully - *Enum tag consistency* — -`+@intFromEnum+` matches C header `+#define+` values - -=== Contributing - -When modifying the ABI/FFI: - -[arabic] -. *Update ABI first* (`+src/ConnABI/*.idr+`) -* Modify type definitions and proofs -* Ensure roundtrip proofs still hold -* Maintain backward compatibility -. *Regenerate C header* (`+generated/abi/.h+`) -* Update tag `+#define+` values -* Update function declarations -. *Update FFI implementation* (`+ffi/zig/src/.zig+`) -* Implement new functions -* Match ABI types exactly -. *Add tests* (`+ffi/zig/test/_test.zig+`) -* Lifecycle tests for new transitions -* Enum tag consistency for new variants -* NULL safety for new functions -. *Update documentation* -* Design doc in `+docs/design/+` -* TOPOLOGY.md completion dashboard -* STATE.a2ml milestones - -=== License - -SPDX-License-Identifier: CC-BY-SA-4.0 - -Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) - -=== See Also - -* link:connectors/README.adoc[Connector overview] -* link:docs/design/DESIGN-2026-03-01-connector-abi-ffi.md[Design -document] -* https://idris2.readthedocs.io[Idris2 Documentation] -* https://ziglang.org/documentation/master/[Zig Documentation] +# Build the Idris2 package (from the package directory) +(cd protocols/proven-dns && idris2 --build proven-dns.ipkg) + +# Build and run that package's Zig tests +(cd protocols/proven-dns/ffi/zig && zig build test) +---- + +These commands establish evidence only for the named package and the toolchain +used. At repository level, the `Justfile` provides `build-idris`, `build-zig`, +`test-zig`, and `test-static`; the aggregate `just build` and `just test` fail +when their required compilers are unavailable. Check the task implementation +before relying on a result. + +`bash tests/e2e.sh` is a partial sample sweep, not a complete package matrix. +The scripts `tests/source_smoke_test.sh` and `tests/aspect/security_test.sh` use +source-pattern heuristics; `tests/binding_inventory.sh` checks only inventory +and source policy. None is proof, executable binding conformance, or a +security-certification tool. + +== Requirements before claiming a verified bridge + +. Name the package, model modules, native source, generated files, and binding. +. Record exact compiler versions and reproducible build commands. +. Generate or verify the C ABI from its declared source of truth. +. Compare symbol names, calling conventions, integer widths, layouts, ownership, + error behavior, and output-buffer semantics. +. Execute tests against the compiled library, including invalid inputs and + failure paths. +. Treat every unimplemented backend as unavailable and fail closed. +. Keep the evidence revision-specific and scoped; do not generalize one package's + results to the entire monorepo. diff --git a/ARCHITECTURE.adoc b/ARCHITECTURE.adoc index 1c0a7a69..ddd1e57a 100644 --- a/ARCHITECTURE.adoc +++ b/ARCHITECTURE.adoc @@ -1,48 +1,73 @@ -== Architecture +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += Architecture and Repository Layout +:toc: +:sectnums: +:revdate: 2026-09-27 -=== Overview +== Overview -This repository follows a modular, maintainable architecture designed -for clarity, scalability, and long-term sustainability. +`proven-servers` is a source monorepo of protocol models, Idris2 packages, Zig +FFI prototypes, connector code, language-binding sources, and documentation. +Package boundaries and implementation layers vary. The repository does not +provide a single executable server or a uniform cross-language ABI. -=== Directory Structure +== Top-level areas -.... -. -├── src/ # Source code -├── tests/ # Test suites -├── docs/ # Documentation -├── scripts/ # Utility scripts -├── config/ # Configuration files -├── LICENSE # License file -├── LICENSES/ # Full license texts -└── README.adoc # Project documentation -.... +[cols="1,3",options="header"] +|=== +|Path |Role and limitation -=== Design Principles +|`protocols/` +|Protocol-named packages; directory presence does not establish complete +protocol implementation or conformance. -* *Separation of Concerns*: Each module has a single responsibility -* *Testability*: Code is written to be easily testable -* *Documentation*: All public APIs are documented -* *Configuration*: Environment-specific settings are externalized +|`core/` +|Shared package models and native code; each package has its own evidence. -=== Dependencies +|`connectors/` +|Integration packages with varying Idris2/Zig/C coverage; no common tested +connector contract is assumed. -* External dependencies are minimized and clearly declared -* Version pinning is used for reproducibility +|`bindings/` +|Language-specific wrappers and scaffolds; inventory is not operational support. -=== Security Considerations +|`ffi/`, `generated/`, `src/` +|Root-level interface examples and ABI artifacts; generated/hand-maintained +status varies by package. -* Sensitive data is never committed to the repository -* Secrets are managed through environment variables or secure vaults -* Regular dependency audits are performed +|`not-proven/` +|Explicitly non-proven prototypes and examples. -=== Maintainability +|`tests/` +|Selected source-pattern, inventory, and package test harnesses; not a full +network interoperability suite. -* Code follows consistent style guidelines -* Pull requests require review and CI checks -* Issues and discussions are tracked transparently +|`.machine_readable/` +|Current state, evidence limits, maintenance policy, capability profile, and +agent guidance. +|=== -''''' +== Build and verification -_Last updated: 2026-07-18_ +The root `Justfile` discovers Idris2 manifests and Zig build files under +`protocols/`, `core/`, and `connectors/`, plus the root FFI example. Individual +packages may require different compiler versions. The root `mise.toml` pins +Just only; it does not yet define a reproducible Idris2/Zig matrix. + +A successful Idris2 build is evidence only for the checked model propositions. +It does not prove an independently written FFI, generated header, language +binding, or wire implementation equivalent. Static smoke checks are +source-pattern heuristics, not proof or runtime tests. See `READINESS.adoc`, +`PROOF-NEEDS.adoc`, and `TEST-NEEDS.adoc`. + +== Security and deployment + +Root container, release, Pages publishing, and cloud deployment workflows are +not configured as product deliverables. The experimental NESY connector's +Fly.io/container paths are disabled. No production image or network service is +claimed. + +Security-related workflows and source checks are configuration, not passing +results. See `SECURITY.adoc` and `docs/THREAT-MODEL.adoc` for the current +reporting route and threat scope. diff --git a/Containerfile b/Containerfile deleted file mode 100644 index 19c7c48e..00000000 --- a/Containerfile +++ /dev/null @@ -1,45 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# -# Containerfile for proven-servers -# Build: podman build -t proven-servers:latest -f Containerfile . -# Run: podman run --rm -it proven-servers:latest -# Seal: selur seal proven-servers:latest - -# --- Build stage --- -FROM cgr.dev/chainguard/wolfi-base:latest AS build - -# Install Idris2 and Zig build dependencies -RUN apk add --no-cache \ - build-base \ - zig \ - gmp-dev \ - curl \ - git \ - bash - -# Install Idris2 (from pack or source) -# Note: Adjust this if a Wolfi package for Idris2 becomes available -RUN curl -sSL https://raw.githubusercontent.com/stefan-hoeck/idris2-pack/main/install.bash | bash -ENV PATH="/root/.pack/bin:${PATH}" - -WORKDIR /build -COPY . . - -# Build Idris2 ABI definitions and type-check all packages -RUN pack typecheck proven-servers.ipkg || true - -# Build Zig FFI shared library -RUN cd ffi/zig && zig build -Doptimize=ReleaseSafe - -# --- Runtime stage --- -FROM cgr.dev/chainguard/static:latest - -# Copy Zig FFI shared library from build stage -COPY --from=build /build/ffi/zig/zig-out/lib/ /usr/local/lib/ -COPY --from=build /build/ffi/zig/zig-out/bin/ /usr/local/bin/ - -# Non-root user (chainguard images default to nonroot) -USER nonroot - -ENTRYPOINT ["/usr/local/bin/proven_servers"] diff --git a/EXPLAINME.adoc b/EXPLAINME.adoc index 2a09da3f..5e62caa4 100644 --- a/EXPLAINME.adoc +++ b/EXPLAINME.adoc @@ -1,82 +1,88 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -= proven-servers — Formally Verified Server Cores — Show Me The Receipts += proven-servers — Evidence and Limitations :toc: -:icons: font - -The README makes claims. This file backs them up with evidence from real code. - -== Core Claims & Evidence - -=== Claim 1: "108 Formally Verified Server Components" - -**From README** (lines 9-11): -____ -A catalog of **108 formally verified server components** written in Idris 2 with dependent types: **94 protocol skeletons**, **8 core primitives**, and **6 connector interfaces**. -____ - -**Evidence**: `/var/mnt/eclipse/repos/proven-servers/protocols/` contains 94 protocol directories (dns, smtp, httpd, mqtt, amqp, etc.). Each protocol defines machine-checked type safety via Idris2 dependent types. No `believe_me` escape hatches permitted. - -**Caveat**: Verification scope is *type system correctness* (message formats, state transitions). Does not verify operational security properties (timing attacks, cryptographic algorithm strength, deployment configuration). - -=== Claim 2: "Every Type Is Total — No Escape Hatches" - -**From README** (lines 120-122): -____ -4. **Proven.** Every type is total. No `believe_me`. No `assert_total`. No escape hatches. -5. **Permanent.** Protocol types don't change. DNS message types from 1987 are still the same types today. This code is meant to last. -____ - -**Evidence**: `/var/mnt/eclipse/repos/proven-servers/src/abi/Types.idr` defines protocol message types using Idris2 total functions. Proof that `rmdir(mkdir(p, fs)) = fs` (reverse operations) exists in `/var/mnt/eclipse/repos/proven-servers/src/abi/Proofs.idr`. - -**Caveat**: Proofs are about *abstract models* (memory-safe type structure). The Zig FFI implementation (`ffi/zig/src/main.zig`) is NOT formally connected to the ABI proofs via mechanized extraction—mapping is manual. - -=== Claim 3: "ABI-FFI Standard: 20 Language Bindings" - -**From README** (lines 124-130): -____ -* **ABI** (Idris 2): Interface definitions with dependent type proofs → `abi/` -* **FFI** (Zig): C-compatible implementation → `ffi/` -* **Bindings**: Thin wrappers for 20 languages → `bindings/` -____ - -**Evidence**: `bindings/` contains 20 language bindings — Ada, C++, C#, Dart, Elixir, Gleam, Go, Haskell, Java, JavaScript, Julia, Kotlin, Lua, OCaml, PHP, Python, ReScript, Ruby, Rust, Swift. Each calls the generated C ABI in `generated/abi/*.h` (per-protocol `libproven_`). Ada (`pragma Import (C, …)`) and Java (JNI) are fully FFI-wired reference bindings; Elixir, Gleam and ReScript are currently constants-only scaffolds with FFI wiring pending — their unproven reimplementations were removed (see `docs/decisions/0003-keep-bindings-thin-abi-wrappers.md`). Per-binding tiers: `.machine_readable/BINDINGS.a2ml`. - -**Caveat**: Bindings are thin (mostly type mirrors). Language-specific type safety depends on that language's type system matching the C header contract—no formal proof of equivalence across language boundaries (yet). - -== Dogfooded Across The Account - -Uses the hyperpolymath ABI/FFI standard (Idris2 ABI + Zig FFI). Same architectural pattern deployed across: -- https://github.com/hyperpolymath/burble[burble] — Elixir control plane with Zig NIFs -- https://github.com/hyperpolymath/gossamer[gossamer] — Window management system with Zig FFI -- https://github.com/hyperpolymath/stapeln[stapeln] — Container validation engine (upcoming Idris2 integration) -- https://github.com/hyperpolymath/robodog-ecm[robodog-ecm] — ECM protocols with Idris2 ABI layer - -This standardization ensures verified components can be composed across projects. - -== File Map - -[cols="1,3"] -|=== -| Path | Contents & Purpose - -| `core/` | 8 core primitives: socket, frame, fsm, wire, compose, tls, config, audit — foundation types for all protocols - -| `protocols/` | 94 protocol skeleton directories (proven-dns/, proven-mqtt/, proven-amqp/, etc.) — each with complete Idris2 type definitions and integration tests - -| `connectors/` | 6 connector interface definitions (dbconn, authconn, cacheconn, queueconn, resolverconn, storageconn) — each with ABI proof + FFI implementation - -| `src/abi/` | Idris2 formal proof files: Types.idr (message/state definitions), Layout.idr (memory layout proofs), Foreign.idr (FFI boundary contracts), Proofs.idr (reversibility theorems) - -| `ffi/zig/` | Zig FFI implementation bridging Idris2 ABI to C: src/main.zig (runtime ops), test/integration_test.zig (ABI conformance tests) - -| `generated/abi/` | Auto-generated C headers (one per connector) — contracts that any language can call - -| `bindings/` | 20 language-specific thin wrappers over the C ABI (ada/, cpp/, csharp/, dart/, elixir/, gleam/, go/, haskell/, java/, javascript/, julia/, kotlin/, lua/, ocaml/, php/, python/, rescript/, ruby/, rust/, swift/). No safety logic in bindings — see ADR 0003 + `.machine_readable/BINDINGS.a2ml` - -| `test/` | Test suite verifying protocol type correctness across all 94 protocols -|=== - -== Questions? - -Open an issue or reach out at j.d.a.jewell@open.ac.uk — happy to explain the formal verification approach in detail. +:sectnums: +:revdate: 2026-09-27 + +[IMPORTANT] +==== +This document does not claim that the repository's protocol packages are +formally verified servers. It distinguishes what is present in source from +what has actually been built, tested, or shown to conform. +==== + +== What the repository contains + +The repository contains protocol-named package directories, selected Idris2 +models, Zig FFI prototypes, connector sources, and language-binding +scaffolding. The counts in `README.adoc` are a directory inventory only. They +do not establish that a directory builds, implements a complete protocol, or +provides an operational server. + +== Model and proof evidence + +Some Idris2 packages declare data types, transition relations, tag maps, or +proof terms. Those declarations become compiler-checked evidence only when the +relevant package builds with Idris2. The current assessment workspace does not +have Idris2 installed, so no current-checkout Idris2 build result is claimed. + +Even a successful Idris2 build would establish only the propositions included +in that package. It would not, by itself, prove correspondence with a Zig +implementation, C header, binding, wire protocol, cryptographic operation, or +running service. Package-specific evidence and gaps are tracked in +`READINESS.adoc`, `PROOF-NEEDS.adoc`, and the package README files. + +== Native interfaces and bindings + +`ffi/`, per-package `ffi/zig/` trees, `generated/abi/`, and `bindings/` +contain code and declarations with varying scope. A header being stored under a +`generated/` directory does not show that a reproducible generator produced +it. Native linking and cross-language ABI equivalence have not been verified +as a repository-wide matrix. + +The language-directory count in `README.adoc` is an inventory, not a support +promise. See `.machine_readable/BINDINGS.a2ml` for conservative source-level +status and `ABI-FFI-README.adoc` for the current interface evidence boundary. +The OCaml native path is explicitly unavailable until compatible C stubs are +implemented; Java/Kotlin declarations alone do not establish JNI +implementations. + +== Tests and current execution status + +`Justfile` defines discovered Idris2 and Zig build/test loops plus explicitly +labelled static smoke checks. The tests under `tests/` are partial executable +samples or source-pattern checks; they are not an exhaustive protocol suite, +security certification, or proof of conformance. + +The assessment workspace used for this update did not have Just, Idris2, or Zig +installed. Consequently, the configured compiler/test matrix has not been run +on this checkout. Historical audit reports under `audits/` apply only to their +recorded revisions and tools. + +== External services and deployment + +The repository does not establish that experimental connectors can reach their +assumed upstreams, authenticate correctly, or persist data. Endpoint names, +source comments, environment-variable lists, and integration stubs are not +runtime evidence. + +Root container/cloud deployment, release, and Pages publishing operations are +not configured as product deliverables. The NESY connector's old Fly.io and +container paths are explicitly disabled. No production server, image, or +release artifact should be inferred from this source tree. + +== Evidence to collect before stronger claims + +. Build the exact package manifests with recorded compiler versions. +. Run package-specific executable tests and relevant negative/error-path tests. +. Generate or independently compare C headers, symbols, layouts, and ABI + boundaries against the corresponding language declarations. +. Exercise real wire encoders/decoders and protocol state machines against + independent fixtures or peer implementations. +. Validate cryptographic operations with appropriate test vectors and review. +. For any proposed service, test authentication, upstream behavior, resource + limits, health/readiness, and failure modes before packaging or deployment. + +See `READINESS.adoc` and `PROOF-NEEDS.adoc` for the current project-wide +verification plan. diff --git a/GOVERNANCE.adoc b/GOVERNANCE.adoc index 3636e8c3..0e4420b7 100644 --- a/GOVERNANCE.adoc +++ b/GOVERNANCE.adoc @@ -1,176 +1,57 @@ -== Project Governance - -This document describes the governance model for *proven-servers*. - -''''' - -=== Project Governance Model - -proven-servers follows a *Benevolent Dictator For Life (BDFL)* -governance model. This model is well-suited for solo maintainers and -small project teams where rapid, consistent decision-making is more -valuable than formal consensus processes. - -The BDFL has final authority on all project decisions, including -technical direction, release schedules, contributor access, and -community standards. - -____ -*Transition clause:* When the core team exceeds three active -maintainers, this project should transition to a *consensus-based -governance model* with documented voting procedures. That transition -should itself be recorded as an Architecture Decision Record (ADR) in -`+docs/decisions/+`. -____ - -''''' - -=== Decision Making - -==== Day-to-day decisions - -* The BDFL makes final decisions on all matters. -* Routine decisions (bug fixes, dependency updates, minor improvements) -may be made by any maintainer with commit access. -* Maintainers are expected to use good judgement and seek input on -non-trivial changes. - -==== Proposing changes - -* Contributors can propose changes by opening issues or pull requests. -* Significant changes (new features, breaking changes, architectural -shifts) should be discussed in an issue before implementation begins. -* The BDFL will provide a clear accept/reject decision with reasoning. - -==== Architecture Decision Records (ADRs) - -* Significant technical decisions are documented as ADRs in -`+docs/decisions/+`. -* ADR statuses: `+proposed+`, `+accepted+`, `+deprecated+`, -`+superseded+`, `+rejected+`. -* ADRs provide a historical record of why decisions were made and what -alternatives were considered. -* See `+.machine_readable/META.a2ml+` for the machine-readable ADR -index. - -''''' - -=== Roles - -==== BDFL (Benevolent Dictator For Life) - -* The project creator and ultimate decision-maker. -* Sets the project’s technical direction and long-term vision. -* Has final say on all matters, including maintainer appointments and -removals. -* Responsible for ensuring the project adheres to RSR standards. - -==== Maintainer - -* Has commit access to the repository. -* Reviews and merges pull requests. -* Triages issues and manages releases. -* Upholds code quality, security standards, and the Code of Conduct. -* Listed in MAINTAINERS.md. - -==== Contributor - -* Anyone who submits pull requests, opens issues, or participates in -discussions. -* Does not have direct commit access. -* Contributions are reviewed by maintainers before merging. -* All contributors must follow the link:CODE_OF_CONDUCT.md[Code of -Conduct]. - -==== Bot - -* Automated agents managed via your bot orchestration system. -* Perform automated code review, security scanning, dependency updates, -and standards enforcement. -* Bot actions are subject to the same quality and review standards as -human contributions. -* Configure your bots in `+.machine_readable/bot_directives/+`. - -''''' - -=== Becoming a Maintainer - -A contributor may be nominated to become a maintainer when they -demonstrate: - -[arabic] -. *Sustained quality contributions* – a track record of well-crafted -pull requests that follow project conventions and require minimal -revision. -. *Understanding of RSR standards* – familiarity with the Repository -Structure Requirements, security policies, and CI/CD workflows used -across the project. -. *Constructive participation* – helpful issue triage, thoughtful code -review comments, and mentoring of other contributors. -. *Reliability* – consistent engagement over a meaningful period -(typically 3+ months of active contribution). - -==== Process - -[arabic] -. An existing maintainer nominates the candidate by opening a private -discussion with the BDFL. -. The BDFL reviews the candidate’s contribution history and community -interactions. -. The BDFL approves or declines the nomination, with reasoning provided -to the nominator. -. If approved, the new maintainer is added to MAINTAINERS.md and granted -appropriate repository access. - -''''' - -=== Removing a Maintainer - -A maintainer may be removed under the following circumstances: - -* *Inactivity*: No meaningful contributions or reviews for 12 or more -consecutive months. The maintainer will be contacted before removal and -offered the option to move to emeritus status voluntarily. -* *Code of Conduct violation*: Behaviour that violates the -link:CODE_OF_CONDUCT.md[Code of Conduct], as determined through the -enforcement process described therein. -* *BDFL discretion*: The BDFL may remove a maintainer for other reasons -(e.g., repeated disregard for project standards, loss of trust). -Reasoning will be documented privately. - -Removed maintainers are moved to the Emeritus section of MAINTAINERS.md -unless removal was due to a serious Code of Conduct violation. - -''''' - -=== Code of Conduct - -All participants in this project are expected to follow the -link:CODE_OF_CONDUCT.md[Code of Conduct]. The Code of Conduct applies to -all project spaces, including issues, pull requests, discussions, and -any forum where the project is represented. - -Enforcement of the Code of Conduct is described in that document. The -BDFL serves as the final arbiter in conduct disputes. - -''''' - -=== Amendments - -This governance document may be amended by the BDFL at any time. All -amendments will be: - -[arabic] -. Documented as an ADR in `+docs/decisions/+` explaining the rationale -for the change. -. Committed to the repository with a clear commit message. -. Communicated to existing maintainers and contributors via the -project’s usual channels. - -Substantive changes (e.g., changing the governance model itself) should -be discussed with the community before adoption, even though the BDFL -retains final authority. - -''''' - -Copyright (c) 2026 hyperpolymath. Licensed under MPL-2.0. +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += Project Governance — proven-servers +:toc: +:sectnums: +:revdate: 2026-09-27 + +== Current model + +This repository currently has one owner and lead maintainer, Jonathan D.A. +Jewell (`@hyperpolymath`), as listed in `MAINTAINERS`. All changes, including dependency and CI workflow updates, +are reviewed and merged by the repository owner. Contributors submit changes +through pull requests; direct pushes to the default branch are limited to the +owner under the current repository policy. + +This is a single-maintainer arrangement, not a multi-person consensus process +or a formal BDFL constitution. Repository settings and access controls are +administered by the hosting platform and are not independently verified by +this document. + +== Decision making + +* Routine changes are proposed in pull requests with scope, evidence, and + limitations stated. +* Significant architectural or security changes should be discussed in an + issue or design note before implementation when practical. +* The lead maintainer makes the merge decision and may request changes or + reject a proposal. `docs/decisions/` contains selected architecture records; + it is not a complete log of every decision. +* Future maintainers may be invited by the repository owner. Adding maintainers + requires an explicit access change and an update to the maintainer records. +* If the maintainer team grows, governance should be revisited and documented + before changing the current review/merge authority. + +== Contributions and conduct + +Contributors should read `.github/CONTRIBUTING.md` and follow +`CODE_OF_CONDUCT.adoc`. Security-sensitive reports should use the private +reporting instructions in `SECURITY.adoc`, not a public issue. + +Automated tools may assist with checks or review but do not replace human +maintainer review and do not receive authority to merge by virtue of being +configured in `.machine_readable/bot_directives/`. + +== Records and scope + +* Current owner and contact route: `MAINTAINERS`. +* Owner/maintainer contact and review rule: `MAINTAINERS`. +* Architecture decisions: `docs/decisions/`. +* Project status and evidence boundary: `.machine_readable/6a2/STATE.a2ml`, + `READINESS.adoc`, and `PROOF-NEEDS.adoc`. +* Repository policy and task definitions: `Justfile` and + `.machine_readable/contractiles/`. + +This document describes the intended repository process. It does not claim +that branch protection, required reviews, release automation, or other forge +settings have been audited or enforced. diff --git a/Justfile b/Justfile index e9f49f1e..fc47171a 100644 --- a/Justfile +++ b/Justfile @@ -1,970 +1,395 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# RSR Standard Justfile Template -# https://just.systems/man/en/ -# -# Copy this file to new projects and customize the placeholder values. -# -# Run `just` to see all available recipes -# Run `just cookbook` to generate docs/just-cookbook.adoc -# Run `just combinations` to see matrix recipe options +# Repository tasks for proven-servers. Build/test commands are package-scoped +# and fail when required compilers are unavailable. Static smoke checks are +# labelled separately from compiler-backed verification. set shell := ["bash", "-uc"] set dotenv-load := true set positional-arguments := true -# Import auto-generated contractile recipes +# Optional contractile checks; the file contains only repository-specific, +# non-destructive recipes. import? "contractile.just" -# Project metadata — customize these project := "proven-servers" -version := "0.1.0" -tier := "infrastructure" # 1 | 2 | infrastructure - -# ═══════════════════════════════════════════════════════════════════════════════ -# DEFAULT & HELP -# ═══════════════════════════════════════════════════════════════════════════════ -# Show all available recipes with descriptions +# Show the available, configured repository tasks. default: @just --list --unsorted -# Show detailed help for a specific recipe help recipe="": #!/usr/bin/env bash + set -euo pipefail if [ -z "{{recipe}}" ]; then just --list --unsorted - echo "" - echo "Usage: just help " - echo " just cookbook # Generate full documentation" - echo " just combinations # Show matrix recipes" else - just --show "{{recipe}}" 2>/dev/null || echo "Recipe '{{recipe}}' not found" + just --show "{{recipe}}" fi -# Show this project's info info: - @echo "Project: {{project}}" - @echo "Version: {{version}}" - @echo "RSR Tier: {{tier}}" - @echo "Recipes: $(just --summary | wc -w)" - @[ -f ".machine_readable/STATE.a2ml" ] && grep -oP 'phase\s*=\s*"\K[^"]+' .machine_readable/STATE.a2ml | head -1 | xargs -I{} echo "Phase: {}" || true - -# ═══════════════════════════════════════════════════════════════════════════════ -# INIT — Bootstrap a new project from this template -# ═══════════════════════════════════════════════════════════════════════════════ - -# Interactive project bootstrap — replaces all {{PLACEHOLDER}} tokens -init: #!/usr/bin/env bash set -euo pipefail + echo "Project: {{project}}" + echo "Current phase: $(sed -n 's/^phase = \"\(.*\)\"/\1/p' .machine_readable/6a2/STATE.a2ml | head -1)" + echo "Readiness: $(sed -n 's/^\*\*Current Grade:\*\* \([A-FX]\).*/\1/p' READINESS.adoc | head -1)" + echo "Toolchains: Idris2 and Zig are required for compiler-backed package checks." + +# Build every Idris2 package manifest under protocols/, core/, and connectors/. +# The root module tree and not-proven/ examples are not package manifests in +# this configured build set. +build-idris: + #!/usr/bin/env bash + set -euo pipefail + command -v idris2 >/dev/null 2>&1 || { echo "ERROR: idris2 is required" >&2; exit 127; } + count=0 + while IFS= read -r -d '' package; do + package_dir="$(dirname "$package")" + package_name="$(basename "$package")" + printf '==> idris2 --build %s/%s\n' "$package_dir" "$package_name" + (cd "$package_dir" && idris2 --build "$package_name") + count=$((count + 1)) + done < <(find protocols core connectors -type f -name '*.ipkg' -print0 | sort -z) + [ "$count" -gt 0 ] || { echo "ERROR: no Idris2 package manifests found" >&2; exit 1; } + echo "Built $count Idris2 package manifests." + +# Build the root FFI example and every Zig build manifest under the maintained +# protocol/core/connector trees. not-proven/ is intentionally excluded. +build-zig: + #!/usr/bin/env bash + set -euo pipefail + command -v zig >/dev/null 2>&1 || { echo "ERROR: zig is required" >&2; exit 127; } + count=0 + while IFS= read -r -d '' build_file; do + build_dir="$(dirname "$build_file")" + printf '==> zig build (%s)\n' "$build_dir" + (cd "$build_dir" && zig build) + count=$((count + 1)) + done < <({ find protocols core connectors -type f -name 'build.zig' -print0; printf 'ffi/zig/build.zig\0'; } | sort -z) + [ "$count" -gt 0 ] || { echo "ERROR: no Zig build manifests found" >&2; exit 1; } + echo "Built $count Zig packages/examples." + +# Build all configured Idris2 and Zig packages. Both compilers are required. +build: build-idris build-zig + +# There is no uniform release profile across package-specific builds. +build-release: + @echo "ERROR: no repository-wide release profile is configured; use the package's documented Zig options." >&2 + @exit 2 + +# There is no repository-wide incremental/watch build target. +build-watch: + @echo "ERROR: no repository-wide build-watch target is configured." >&2 + @exit 2 - echo "═══════════════════════════════════════════════════" - echo " RSR Project Bootstrap" - echo "═══════════════════════════════════════════════════" - echo "" - - # --- Load defaults from config (if exists) --- - # Create yours: ~/.config/rsr/defaults - # Format: OWNER=myorg AUTHOR="My Name" AUTHOR_EMAIL=me@example.org ... - DEFAULTS="${XDG_CONFIG_HOME:-$HOME/.config}/rsr/defaults" - if [ -f "$DEFAULTS" ]; then - echo "Loading defaults from $DEFAULTS" - # shellcheck source=/dev/null - source "$DEFAULTS" - echo "" - fi - - # --- Required values (pre-filled from defaults if available) --- - read -rp "Project name (human-readable, e.g. My Project): " PROJECT_NAME - [ -z "$PROJECT_NAME" ] && echo "Error: project name required" && exit 1 +# Remove compiler outputs only from maintained source/package trees. +clean: + #!/usr/bin/env bash + set -euo pipefail + find protocols core connectors bindings ffi -type d \( -name build -o -name zig-out -o -name _build \) -prune -exec rm -rf {} + + rm -rf target/ _build/ dist/ out/ obj/ bin/ - read -rp "Repository slug (e.g. my-project): " REPO - [ -z "$REPO" ] && echo "Error: repo slug required" && exit 1 +clean-all: clean + rm -rf .cache .tmp - read -rp "Owner [${OWNER:-}]: " _OWNER - OWNER="${_OWNER:-${OWNER:-}}" - [ -z "$OWNER" ] && echo "Error: owner required" && exit 1 +# Run Zig tests for every discovered build manifest and the root FFI example. +test-zig: + #!/usr/bin/env bash + set -euo pipefail + command -v zig >/dev/null 2>&1 || { echo "ERROR: zig is required" >&2; exit 127; } + count=0 + while IFS= read -r -d '' build_file; do + test_dir="$(dirname "$build_file")" + printf '==> zig build test (%s)\n' "$test_dir" + (cd "$test_dir" && zig build test) + count=$((count + 1)) + done < <({ find protocols core connectors -type f -name 'build.zig' -print0; printf 'ffi/zig/build.zig\0'; } | sort -z) + [ "$count" -gt 0 ] || { echo "ERROR: no Zig test targets found" >&2; exit 1; } + echo "Ran Zig test targets for $count packages/examples." - read -rp "Author full name [${AUTHOR:-}]: " _AUTHOR - AUTHOR="${_AUTHOR:-${AUTHOR:-}}" - [ -z "$AUTHOR" ] && echo "Error: author name required" && exit 1 +# Source-pattern heuristics, selected policy checks, and language inventory; +# these are not substitutes for compiler tests or formal proofs. +test-static: + bash tests/source_smoke_test.sh + bash tests/aspect/security_test.sh + bash tests/binding_inventory.sh - read -rp "Author email [${AUTHOR_EMAIL:-}]: " _AUTHOR_EMAIL - AUTHOR_EMAIL="${_AUTHOR_EMAIL:-${AUTHOR_EMAIL:-}}" - [ -z "$AUTHOR_EMAIL" ] && echo "Error: email required" && exit 1 +test: build-idris test-zig test-static - # --- Optional values (pre-filled from defaults if available) --- - read -rp "Author organization [${AUTHOR_ORG:-none}]: " _AUTHOR_ORG - AUTHOR_ORG="${_AUTHOR_ORG:-${AUTHOR_ORG:-}}" +test-verbose: test - read -rp "Previous/alt email [${AUTHOR_EMAIL_ALT:-none}]: " _AUTHOR_EMAIL_ALT - AUTHOR_EMAIL_ALT="${_AUTHOR_EMAIL_ALT:-${AUTHOR_EMAIL_ALT:-}}" +test-smoke: test-static - read -rp "Project description []: " PROJECT_DESCRIPTION +source-smoke: + bash tests/source_smoke_test.sh - read -rp "Forge domain [${FORGE:-github.com}]: " _FORGE - FORGE="${_FORGE:-${FORGE:-github.com}}" +security-test: + bash tests/aspect/security_test.sh - read -rp "Security contact email [${SECURITY_EMAIL:-$AUTHOR_EMAIL}]: " _SECURITY_EMAIL - SECURITY_EMAIL="${_SECURITY_EMAIL:-${SECURITY_EMAIL:-$AUTHOR_EMAIL}}" +binding-inventory: + bash tests/binding_inventory.sh - read -rp "Conduct contact email [${CONDUCT_EMAIL:-$AUTHOR_EMAIL}]: " _CONDUCT_EMAIL - CONDUCT_EMAIL="${_CONDUCT_EMAIL:-${CONDUCT_EMAIL:-$AUTHOR_EMAIL}}" +e2e: + bash tests/e2e.sh - read -rp "Project type (library|binary|monorepo|service|website) [library]: " PROJECT_TYPE - PROJECT_TYPE="${PROJECT_TYPE:-library}" +# Run configured checks only; language-specific formatting is not configured. +quality: fmt-check lint test + @echo "Configured build, test, shell, and policy checks passed." - read -rp "Website URL [https://${FORGE}/${OWNER}/${REPO}]: " WEBSITE - WEBSITE="${WEBSITE:-https://${FORGE}/${OWNER}/${REPO}}" +# No repository-wide formatter is declared. Do not report a no-op as formatting. +fmt: + @echo "ERROR: no repository-wide formatter is configured." >&2 + @exit 2 - # --- Container values (optional — only relevant if container/ exists) --- - if [ -d "container" ]; then - echo "" - echo "── Container configuration (optional) ─────────" - read -rp "Service name [${REPO}]: " _SERVICE_NAME - SERVICE_NAME="${_SERVICE_NAME:-${REPO}}" - read -rp "Primary port [8080]: " _PORT - PORT="${_PORT:-8080}" - read -rp "Container registry [ghcr.io/${OWNER}]: " _REGISTRY - REGISTRY="${_REGISTRY:-ghcr.io/${OWNER}}" - else - SERVICE_NAME="${REPO}" - PORT="8080" - REGISTRY="ghcr.io/${OWNER}" - fi +# Check whitespace in staged and unstaged changes; not a language formatter. +fmt-check: + git diff --check + git diff --cached --check - # --- Derived values --- - PROJECT_UPPER=$(echo "$REPO" | tr '[:lower:]-' '[:upper:]_') - PROJECT_LOWER=$(echo "$REPO" | tr '[:upper:]-' '[:lower:]_') - CURRENT_YEAR=$(date +%Y) - CURRENT_DATE=$(date +%Y-%m-%d) - VERSION="0.1.0" - - # Derive citation name parts (best-effort split on last space) - AUTHOR_LAST="${AUTHOR##* }" - AUTHOR_FIRST="${AUTHOR% *}" - FIRST_INITIAL="${AUTHOR_FIRST:0:1}." - if [ "$AUTHOR_LAST" = "$AUTHOR_FIRST" ]; then - AUTHOR_FIRST="$AUTHOR" - AUTHOR_LAST="" - FIRST_INITIAL="" - fi +# Validate shell syntax and the binding inventory/scaffold policy. +lint: + #!/usr/bin/env bash + set -euo pipefail + while IFS= read -r -d '' script; do + bash -n "$script" + done < <(find . -path './.git' -prune -o -type f -name '*.sh' -print0) + bash tools/check-binding-policy.sh + echo "Shell syntax and binding-policy checks passed; no complete multi-language lint matrix is configured." - echo "" - echo "── Summary ──────────────────────────────────────" - echo " Project: $PROJECT_NAME" - echo " Repo: $REPO" - echo " Owner: $OWNER" - echo " Author: $AUTHOR <$AUTHOR_EMAIL>" - [ -n "$AUTHOR_ORG" ] && echo " Organization: $AUTHOR_ORG" - echo " Forge: $FORGE" - echo " Year: $CURRENT_YEAR" - echo "────────────────────────────────────────────────" - echo "" - read -rp "Proceed? [Y/n] " CONFIRM - [[ "${CONFIRM:-Y}" =~ ^[Nn] ]] && echo "Aborted." && exit 0 - - echo "" - echo "Replacing placeholders..." - - # Brace tokens as variables (hex avoids just interpolation) - LB=$(printf '\x7b\x7b') - RB=$(printf '\x7d\x7d') - - # Build the sed expression list - # Note: using | as delimiter since URLs contain / - SED_ARGS=( - -e "s|${LB}PROJECT_NAME${RB}|${PROJECT_NAME}|g" - -e "s|${LB}PROJECT_DESCRIPTION${RB}|${PROJECT_DESCRIPTION}|g" - -e "s|${LB}PROJECT${RB}|${PROJECT_UPPER}|g" - -e "s|${LB}project${RB}|${PROJECT_LOWER}|g" - -e "s|${LB}REPO${RB}|${REPO}|g" - -e "s|${LB}OWNER${RB}|${OWNER}|g" - -e "s|${LB}AUTHOR${RB}|${AUTHOR}|g" - -e "s|${LB}AUTHOR_EMAIL${RB}|${AUTHOR_EMAIL}|g" - -e "s|${LB}AUTHOR_ORG${RB}|${AUTHOR_ORG}|g" - -e "s|${LB}AUTHOR_LAST${RB}|${AUTHOR_LAST}|g" - -e "s|${LB}AUTHOR_FIRST${RB}|${AUTHOR_FIRST}|g" - -e "s|${LB}AUTHOR_INITIALS${RB}|${FIRST_INITIAL}|g" - -e "s|${LB}FORGE${RB}|${FORGE}|g" - -e "s|${LB}CURRENT_YEAR${RB}|${CURRENT_YEAR}|g" - -e "s|${LB}CURRENT_DATE${RB}|${CURRENT_DATE}|g" - -e "s|${LB}DATE${RB}|${CURRENT_DATE}|g" - -e "s|${LB}SECURITY_EMAIL${RB}|${SECURITY_EMAIL}|g" - -e "s|${LB}CONDUCT_EMAIL${RB}|${CONDUCT_EMAIL}|g" - -e "s|${LB}LICENSE${RB}|MPL-2.0|g" - -e "s|${LB}CONDUCT_TEAM${RB}|Code of Conduct Committee|g" - -e "s|${LB}RESPONSE_TIME${RB}|48 hours|g" - -e "s|${LB}MAIN_BRANCH${RB}|main|g" - -e "s|${LB}PROJECT_PURPOSE${RB}|${PROJECT_DESCRIPTION}|g" - -e "s|${LB}PROJECT_ROLE${RB}|${PROJECT_TYPE}|g" - -e "s|${LB}PROJECT_TYPE${RB}|${PROJECT_TYPE}|g" - -e "s|${LB}WEBSITE${RB}|${WEBSITE}|g" - -e "s|${LB}SERVICE_NAME${RB}|${SERVICE_NAME}|g" - -e "s|${LB}PORT${RB}|${PORT}|g" - -e "s|${LB}REGISTRY${RB}|${REGISTRY}|g" - -e "s|${LB}IMAGE${RB}|${REGISTRY}/${SERVICE_NAME}|g" - -e "s|${LB}VERSION${RB}|${VERSION}|g" - -e "s|${LB}EMAIL${RB}|${AUTHOR_EMAIL}|g" - ) - [ -n "$AUTHOR_EMAIL_ALT" ] && SED_ARGS+=(-e "s|${LB}AUTHOR_EMAIL_ALT${RB}|${AUTHOR_EMAIL_ALT}|g") - - # Replace in all text files (skip .git, LICENSE text, and binaries) - find . -type f \ - -not -path './.git/*' \ - -not -name 'MPL-2.0.txt' \ - -not -name '*.png' -not -name '*.jpg' -not -name '*.gif' \ - -not -name '*.woff' -not -name '*.woff2' \ - | while read -r file; do - if file --brief "$file" | grep -qi 'text\|ascii\|utf'; then - sed -i "${SED_ARGS[@]}" "$file" +# Report required compiler/task-runner availability; missing tools fail the task. +deps: + #!/usr/bin/env bash + set -euo pipefail + missing=0 + for tool in just idris2 zig; do + if command -v "$tool" >/dev/null 2>&1; then + printf 'available: %s (%s)\n' "$tool" "$(command -v "$tool")" + else + printf 'missing: %s\n' "$tool" >&2 + missing=1 fi done + [ "$missing" -eq 0 ] || exit 127 - # Also replace [YOUR-REPO-NAME] and [YOUR-NAME/ORG] in AI manifest - sed -i "s|\[YOUR-REPO-NAME\]|${PROJECT_NAME}|g" 0-AI-MANIFEST.a2ml 2>/dev/null || true - sed -i "s|\[YOUR-NAME/ORG\]|${OWNER}|g" 0-AI-MANIFEST.a2ml 2>/dev/null || true - - echo "" - echo "── Validation ───────────────────────────────────" - - # Check for remaining placeholders - PATTERN="${LB}[A-Z_]*${RB}" - REMAINING=$(grep -rl "$PATTERN" . --include='*.md' --include='*.adoc' --include='*.yml' --include='*.yaml' --include='*.a2ml' --include='*.toml' --include='*.scm' --include='*.ncl' --include='*.nix' --include='*.json' --include='*.sh' 2>/dev/null | grep -v '.git/' | grep -v 'PLACEHOLDERS.md' || true) - if [ -n "$REMAINING" ]; then - echo "WARNING: Remaining placeholders in:" - echo "$REMAINING" | sed 's/^/ /' - echo "" - echo "Run: grep -rn '$LB' . --include='*.md' to inspect" - else - echo "All placeholders replaced successfully!" - fi - - # K9-SVC validation (if available) - if command -v k9-svc >/dev/null 2>&1; then - echo "" - echo "Running k9-svc validation..." - k9-svc validate . 2>/dev/null || true - fi - - echo "" - echo "Done! Next steps:" - echo " 1. Review changes: git diff" - echo " 2. Remove template cruft: rm PLACEHOLDERS.md" - echo " 3. Customize README.adoc for your project" - echo " 4. Commit: git add -A && git commit -m 'feat: initialize from RSR template'" - echo " 5. Push: git remote add origin git@${FORGE}:${OWNER}/${REPO}.git && git push -u origin main" - -# ═══════════════════════════════════════════════════════════════════════════════ -# BUILD & COMPILE -# ═══════════════════════════════════════════════════════════════════════════════ - -# Build the project (debug mode) -build *args: - @echo "Building {{project}} (debug)..." - # TODO: Replace with your build command - # Examples: - # cargo build {{args}} # Rust - # mix compile {{args}} # Elixir - # zig build {{args}} # Zig - # deno task build {{args}} # Deno/ReScript - @echo "Build complete" - -# Build in release mode with optimizations -build-release *args: - @echo "Building {{project}} (release)..." - # TODO: Replace with your release build command - # Examples: - # cargo build --release {{args}} - # MIX_ENV=prod mix compile {{args}} - # zig build -Doptimize=ReleaseFast {{args}} - @echo "Release build complete" - -# Build and watch for changes (requires entr or similar) -build-watch: - @echo "Watching for changes..." - # TODO: Customize file patterns for your language - # Examples: - # find src -name '*.rs' | entr -c just build - # mix compile --force --warnings-as-errors - # deno task dev - -# Clean build artifacts [reversible: rebuild with `just build`] -clean: - @echo "Cleaning..." - # TODO: Customize for your build system - rm -rf target/ _build/ build/ dist/ out/ obj/ bin/ - -# Deep clean including caches [reversible: rebuild] -clean-all: clean - rm -rf .cache .tmp +# Run a filesystem vulnerability scan; fail if Trivy is not installed. +deps-audit: + #!/usr/bin/env bash + set -euo pipefail + command -v trivy >/dev/null 2>&1 || { echo "ERROR: trivy is required for dependency/filesystem auditing" >&2; exit 127; } + trivy fs --severity HIGH,CRITICAL --exit-code 1 --quiet . -# ═══════════════════════════════════════════════════════════════════════════════ -# TEST & QUALITY -# ═══════════════════════════════════════════════════════════════════════════════ - -# Run all tests -test *args: - @echo "Running tests..." - # TODO: Replace with your test command - # Examples: - # cargo test {{args}} - # mix test {{args}} - # zig build test {{args}} - # deno test {{args}} - @echo "Tests passed!" - -# Run tests with verbose output -test-verbose: - @echo "Running tests (verbose)..." - # TODO: Replace with verbose test command - -# Smoke test -test-smoke: - @echo "Smoke test..." - # TODO: Add basic sanity checks - -# Run all quality checks -quality: fmt-check lint test - @echo "All quality checks passed!" +security: security-test deps-audit + @echo "Configured static smoke checks and Trivy scan passed." -# Fix all auto-fixable issues [reversible: git checkout] -fix: fmt - @echo "Fixed all auto-fixable issues" +# Generate an SPDX JSON SBOM only when Syft is available. +sbom: + #!/usr/bin/env bash + set -euo pipefail + command -v syft >/dev/null 2>&1 || { echo "ERROR: syft is required to generate the SBOM" >&2; exit 127; } + mkdir -p docs/security + syft . -o spdx-json > docs/security/sbom.spdx.json + echo "Generated docs/security/sbom.spdx.json" -# ═══════════════════════════════════════════════════════════════════════════════ -# LINT & FORMAT -# ═══════════════════════════════════════════════════════════════════════════════ +# Generate the Justfile cookbook and man page (not all project documentation). +docs: cookbook man + @echo "Generated Justfile task reference and man page." -# Format all source files [reversible: git checkout] -fmt: - @echo "Formatting source files..." - # TODO: Replace with your formatter - # Examples: - # cargo fmt - # mix format - # gleam format - # deno fmt - -# Check formatting without changes -fmt-check: - @echo "Checking formatting..." - # TODO: Replace with your format check - # Examples: - # cargo fmt --check - # mix format --check-formatted - # gleam format --check - -# Run linter -lint: - @echo "Linting source files..." - # TODO: Replace with your linter - # Examples: - # cargo clippy -- -D warnings - # mix credo --strict - # gleam check - -# ═══════════════════════════════════════════════════════════════════════════════ -# RUN & EXECUTE -# ═══════════════════════════════════════════════════════════════════════════════ - -# Run the application -run *args: build - # TODO: Replace with your run command - echo "Run not configured yet" - -# Run with verbose output -run-verbose *args: build - # TODO: Replace with verbose run command - echo "Run not configured yet" - -# Install to user path -install: build-release - @echo "Installing {{project}}..." - # TODO: Replace with your install command - -# ═══════════════════════════════════════════════════════════════════════════════ -# DEPENDENCIES -# ═══════════════════════════════════════════════════════════════════════════════ - -# Install/check all dependencies -deps: - @echo "Checking dependencies..." - # TODO: Replace with your dependency check - # Examples: - # cargo check - # mix deps.get - # gleam deps download - @echo "All dependencies satisfied" - -# Audit dependencies for vulnerabilities -deps-audit: - @echo "Auditing for vulnerabilities..." - # TODO: Replace with your audit command - # Examples: - # cargo audit - # mix audit - @command -v trivy >/dev/null && trivy fs --severity HIGH,CRITICAL --quiet . || true - @echo "Audit complete" - -# ═══════════════════════════════════════════════════════════════════════════════ -# DOCUMENTATION -# ═══════════════════════════════════════════════════════════════════════════════ - -# Generate all documentation -docs: - @mkdir -p docs/generated docs/man - just cookbook - just man - @echo "Documentation generated in docs/" - -# Generate justfile cookbook documentation cookbook: #!/usr/bin/env bash + set -euo pipefail mkdir -p docs - OUTPUT="docs/just-cookbook.adoc" - echo "= {{project}} Justfile Cookbook" > "$OUTPUT" - echo ":toc: left" >> "$OUTPUT" - echo ":toclevels: 3" >> "$OUTPUT" - echo "" >> "$OUTPUT" - echo "Generated: $(date -Iseconds)" >> "$OUTPUT" - echo "" >> "$OUTPUT" - echo "== Recipes" >> "$OUTPUT" - echo "" >> "$OUTPUT" - just --list --unsorted | while read -r line; do - if [[ "$line" =~ ^[[:space:]]+([a-z_-]+) ]]; then - recipe="${BASH_REMATCH[1]}" - echo "=== $recipe" >> "$OUTPUT" - echo "" >> "$OUTPUT" - echo "[source,bash]" >> "$OUTPUT" - echo "----" >> "$OUTPUT" - echo "just $recipe" >> "$OUTPUT" - echo "----" >> "$OUTPUT" - echo "" >> "$OUTPUT" - fi - done - echo "Generated: $OUTPUT" + output="docs/just-cookbook.adoc" + { + echo '= proven-servers Justfile Cookbook' + echo ':toc: left' + echo ':toclevels: 2' + echo + echo 'This file is generated from the configured root Justfile recipes.' + echo + echo '== Available Recipes' + echo + just --list --unsorted + } > "$output" + echo "Generated $output" -# Generate man page man: #!/usr/bin/env bash + set -euo pipefail mkdir -p docs/man - cat > docs/man/{{project}}.1 << EOF - .TH {{project}} 1 "$(date +%Y-%m-%d)" "{{version}}" "{{project}} Manual" + cat > docs/man/proven-servers.1 <<'EOF' + .TH proven-servers 1 "$(date +%Y-%m-%d)" "proven-servers" "User Commands" .SH NAME - {{project}} \- RSR-compliant project - .SH SYNOPSIS - .B just - [recipe] [args...] + proven-servers \- protocol models and FFI prototype sources .SH DESCRIPTION - RSR (Rhodium Standard Repository) project managed with just. - .SH AUTHOR - $(git config user.name 2>/dev/null || echo "Author") <$(git config user.email 2>/dev/null || echo "email")> + This source repository is not a production server distribution. The Justfile provides package-scoped build and test tasks plus static smoke checks. + .SH SEE ALSO + README.adoc(7), QUICKSTART-DEV.adoc(7) EOF - echo "Generated: docs/man/{{project}}.1" - -# ═══════════════════════════════════════════════════════════════════════════════ -# CONTAINERS (stapeln ecosystem — Podman + Chainguard Wolfi) -# ═══════════════════════════════════════════════════════════════════════════════ + echo "Generated docs/man/proven-servers.1" -# Initialise container templates — substitute placeholders with project values -container-init: +# Validate repository-specific metadata locations and synchronized task copies. +validate-rsr: #!/usr/bin/env bash set -euo pipefail - - if [ ! -d "container" ]; then - echo "Error: container/ directory not found." - echo "This repo may not have been created from rsr-template-repo." - exit 1 - fi - - echo "=== Container Template Initialisation ===" - echo "" - - # Load RSR defaults if available - DEFAULTS="${XDG_CONFIG_HOME:-$HOME/.config}/rsr/defaults" - if [ -f "$DEFAULTS" ]; then - echo "Loading defaults from $DEFAULTS" - # shellcheck source=/dev/null - source "$DEFAULTS" - echo "" - fi - - # Prompt for container-specific values - read -rp "Service name (e.g. my-api) [{{project}}]: " _SERVICE_NAME - SERVICE_NAME="${_SERVICE_NAME:-{{project}}}" - - read -rp "Primary port [8080]: " _PORT - PORT="${_PORT:-8080}" - - read -rp "Container registry [ghcr.io/${OWNER:-hyperpolymath}]: " _REGISTRY - REGISTRY="${_REGISTRY:-ghcr.io/${OWNER:-hyperpolymath}}" - - echo "" - echo " Service: $SERVICE_NAME" - echo " Port: $PORT" - echo " Registry: $REGISTRY" - echo "" - read -rp "Proceed? [Y/n] " CONFIRM - [[ "${CONFIRM:-Y}" =~ ^[Nn] ]] && echo "Aborted." && exit 0 - - echo "" - echo "Replacing container placeholders..." - - # Brace tokens as variables (hex escapes avoid just interpolation) - LB=$(printf '\x7b\x7b') - RB=$(printf '\x7d\x7d') - - SED_ARGS=( - -e "s|${LB}SERVICE_NAME${RB}|${SERVICE_NAME}|g" - -e "s|${LB}PORT${RB}|${PORT}|g" - -e "s|${LB}REGISTRY${RB}|${REGISTRY}|g" + required=( + .editorconfig .gitattributes .gitignore mise.toml Justfile + README.adoc LICENSE SECURITY.adoc MAINTAINERS .github/CODEOWNERS + 0-AI-MANIFEST.a2ml .well-known/security.txt + .machine_readable/6a2/AGENTIC.a2ml + .machine_readable/6a2/ECOSYSTEM.a2ml + .machine_readable/6a2/META.a2ml + .machine_readable/6a2/NEUROSYM.a2ml + .machine_readable/6a2/PLAYBOOK.a2ml + .machine_readable/6a2/STATE.a2ml + .machine_readable/6a2/anchor/ANCHOR.a2ml + .machine_readable/rsr-profile.a2ml + .machine_readable/BINDINGS.a2ml + .machine_readable/contractiles/Adjustfile.a2ml + .machine_readable/contractiles/Intentfile.a2ml + .machine_readable/contractiles/Mustfile.a2ml + .machine_readable/contractiles/Trustfile.a2ml + .machine_readable/contractiles/Justfile + .machine_readable/policies/MAINTENANCE-AXES.a2ml + .machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml + .machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml + .machine_readable/ai/README.adoc + .machine_readable/bot_directives/README.adoc + docs/maintenance/MAINTENANCE-CHECKLIST.adoc + docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc + docs/AI-CONVENTIONS.adoc ) - - find container/ -type f | while read -r file; do - if file --brief "$file" | grep -qi 'text\|ascii\|utf'; then - sed -i "${SED_ARGS[@]}" "$file" + missing=0 + for path in "${required[@]}"; do + if [ ! -e "$path" ]; then + printf 'MISSING: %s\n' "$path" >&2 + missing=1 fi done - - echo "Container templates initialised." - echo "" - echo "Next steps:" - echo " 1. Edit container/Containerfile — add your build commands" - echo " 2. Edit container/entrypoint.sh — set your application binary" - echo " 3. Review container/compose.toml — adjust services and volumes" - echo " 4. Build: just container-build" - -# Build container image via cerro-torre pipeline -container-build *args: - #!/usr/bin/env bash - if [ -f "container/ct-build.sh" ]; then - cd container && ./ct-build.sh {{args}} - elif [ -f "container/Containerfile" ]; then - podman build -t {{project}}:latest -f container/Containerfile . - elif [ -f "Containerfile" ]; then - podman build -t {{project}}:latest -f Containerfile . - else - echo "No Containerfile found in container/ or project root" - exit 1 + for name in STATE META ECOSYSTEM AGENTIC NEUROSYM PLAYBOOK; do + if [ -e ".machine_readable/$name.a2ml" ]; then + printf 'DUPLICATE CORE METADATA: .machine_readable/%s.a2ml\n' "$name" >&2 + missing=1 + fi + done + if ! cmp -s Justfile .machine_readable/contractiles/Justfile; then + echo "MISMATCH: .machine_readable/contractiles/Justfile is not synchronized with Justfile" >&2 + missing=1 fi + [ "$missing" -eq 0 ] || exit 1 + echo "Repository metadata paths and Justfile copy are consistent." -# Verify compose configuration -container-verify: +# Validate only basic required fields; this is not a complete A2ML parser. +validate-state: #!/usr/bin/env bash - if [ ! -f "container/compose.toml" ]; then - echo "No container/compose.toml found" - exit 1 - fi - cd container - if command -v selur-compose &>/dev/null; then - selur-compose verify - else - echo "selur-compose not found, falling back to podman compose" - podman compose --file compose.toml config - fi - -# Start container stack -container-up *args: + set -euo pipefail + state=.machine_readable/6a2/STATE.a2ml + test -f "$state" + grep -q '^\[metadata\]$' "$state" + grep -q '^project = "proven-servers"$' "$state" + grep -Eq '^last-updated = "[0-9]{4}-[0-9]{2}-[0-9]{2}"$' "$state" + grep -q '^\[position\]$' "$state" + grep -Eq '^phase = "[^"]+"$' "$state" + echo "STATE.a2ml has the required project and position fields (syntax not fully parsed)." + +# Ensure AI-facing onboarding warns about scope and points to real developer steps. +validate-onboarding: #!/usr/bin/env bash - if [ ! -f "container/compose.toml" ]; then - echo "No container/compose.toml found" + set -euo pipefail + guide=docs/AI_INSTALLATION_GUIDE.adoc + test -f "$guide" + grep -q 'not a production server' "$guide" + grep -q 'QUICKSTART-DEV.adoc' "$guide" + if grep -q 'TODO-AI-INSTALL' "$guide"; then + echo "ERROR: obsolete AI-install placeholders remain in $guide" >&2 exit 1 fi - cd container - if command -v selur-compose &>/dev/null; then - selur-compose up {{args}} - else - podman compose --file compose.toml up {{args}} - fi + echo "AI-assisted developer onboarding states its deployment boundary." -# Stop container stack -container-down: - #!/usr/bin/env bash - cd container 2>/dev/null || { echo "No container/ directory"; exit 1; } - if command -v selur-compose &>/dev/null; then - selur-compose down - else - podman compose --file compose.toml down - fi - -# Sign and verify container bundle (build + pack + sign + verify) -container-sign: - #!/usr/bin/env bash - if [ -f "container/ct-build.sh" ]; then - cd container && ./ct-build.sh - else - echo "No container/ct-build.sh found" - exit 1 - fi +validate: validate-rsr validate-state validate-onboarding -# Push signed bundle to registry -container-push: +state-touch: #!/usr/bin/env bash - if [ -f "container/ct-build.sh" ]; then - cd container && ./ct-build.sh --push - else - echo "No container/ct-build.sh found — falling back to podman push" - podman push {{project}}:latest - fi - -# Run container interactively (for debugging) -container-run *args: - podman run --rm -it {{project}}:latest {{args}} + set -euo pipefail + state=.machine_readable/6a2/STATE.a2ml + sed -i "s/^last-updated = \"[^"]*\"/last-updated = \"$(date +%Y-%m-%d)\"/" "$state" + echo "Updated $state timestamp." -# ═══════════════════════════════════════════════════════════════════════════════ -# CI & AUTOMATION -# ═══════════════════════════════════════════════════════════════════════════════ +state-phase: + @sed -n 's/^phase = "\(.*\)"/\1/p' .machine_readable/6a2/STATE.a2ml | head -1 -# Run full CI pipeline locally -ci: deps quality - @echo "CI pipeline complete!" +# Run all configured local quality gates; requires the declared toolchains. +ci: quality security + @echo "Configured local CI tasks passed." -# Install git hooks install-hooks: - @mkdir -p .git/hooks - @cat > .git/hooks/pre-commit << 'HOOKEOF' - #!/bin/bash - just fmt-check || exit 1 - just lint || exit 1 - HOOKEOF - @chmod +x .git/hooks/pre-commit - @echo "Git hooks installed" - -# ═══════════════════════════════════════════════════════════════════════════════ -# SECURITY -# ═══════════════════════════════════════════════════════════════════════════════ - -# Run security audit -security: deps-audit - @echo "=== Security Audit ===" - @command -v trivy >/dev/null && trivy fs --severity HIGH,CRITICAL . || true - @echo "Security audit complete" - -# Generate SBOM -sbom: - @mkdir -p docs/security - @command -v syft >/dev/null && syft . -o spdx-json > docs/security/sbom.spdx.json || echo "syft not found" - -# ═══════════════════════════════════════════════════════════════════════════════ -# VALIDATION & COMPLIANCE -# ═══════════════════════════════════════════════════════════════════════════════ - -# Validate RSR compliance -validate-rsr: #!/usr/bin/env bash - echo "=== RSR Compliance Check ===" - MISSING="" - for f in .editorconfig .gitignore Justfile README.adoc LICENSE; do - [ -f "$f" ] || MISSING="$MISSING $f" - done - for f in .machine_readable/STATE.a2ml .machine_readable/META.a2ml .machine_readable/ECOSYSTEM.a2ml .machine_readable/anchors/ANCHOR.a2ml .machine_readable/policies/MAINTENANCE-AXES.a2ml .machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml .machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml; do - [ -f "$f" ] || MISSING="$MISSING $f" - done - for f in docs/maintenance/MAINTENANCE-CHECKLIST.md docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc; do - [ -f "$f" ] || MISSING="$MISSING $f" - done - if [ -f ".machine_readable/META.a2ml" ]; then - grep -q 'axis-1 = "must > intend > like"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:axis-1" - grep -q 'axis-2 = "corrective > adaptive > perfective"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:axis-2" - grep -q 'axis-3 = "systems > compliance > effects"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:axis-3" - grep -q 'scoping-first = true' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:scoping-first" - grep -q 'idris-unsound-scan = "believe_me/assert_total"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:idris-unsound-scan" - grep -q 'audit-focus = "systems in place, documentation explains actual state, safety/security accounted for, observed effects reviewed"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:audit-focus" - grep -q 'compliance-focus = "seams/compromises/exception register, bounded exceptions, anti-drift checks"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:compliance-focus" - grep -q 'effects-evidence = "benchmark execution/results and maintainer status dialogue/review"' .machine_readable/META.a2ml || MISSING="$MISSING META.a2ml:effects-evidence" - grep -q 'compliance-tooling = "panic-attack"' .machine_readable/policies/MAINTENANCE-AXES.a2ml || MISSING="$MISSING MAINTENANCE-AXES.a2ml:compliance-tooling" - grep -q 'effects-tooling = "ecological checking with sustainabot guidance"' .machine_readable/policies/MAINTENANCE-AXES.a2ml || MISSING="$MISSING MAINTENANCE-AXES.a2ml:effects-tooling" - grep -q 'source-human = "docs/maintenance/MAINTENANCE-CHECKLIST.md"' .machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml || MISSING="$MISSING MAINTENANCE-CHECKLIST.a2ml:source-human" - grep -q 'source-human = "docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc"' .machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml || MISSING="$MISSING SOFTWARE-DEVELOPMENT-APPROACH.a2ml:source-human" - fi - if [ -n "$MISSING" ]; then - echo "MISSING:$MISSING" - exit 1 - fi - echo "RSR compliance: PASS" + set -euo pipefail + hooks_dir="$(git rev-parse --git-path hooks)" + mkdir -p "$hooks_dir" + cat > "$hooks_dir/pre-commit" <<'HOOK' + #!/usr/bin/env bash + set -euo pipefail + just fmt-check + just lint + HOOK + chmod +x "$hooks_dir/pre-commit" + echo "Installed pre-commit hook at $hooks_dir/pre-commit" -# Validate STATE.a2ml syntax -validate-state: - @if [ -f ".machine_readable/STATE.a2ml" ]; then \ - grep -q '^\[metadata\]' .machine_readable/STATE.a2ml && \ - grep -q 'project\s*=' .machine_readable/STATE.a2ml && \ - echo "STATE.a2ml: valid" || echo "STATE.a2ml: INVALID (missing required sections)"; \ - else \ - echo "No .machine_readable/STATE.a2ml found"; \ - fi +# Run the standards scanner only when installed; absence is an explicit error. +assail: + @command -v panic-attack >/dev/null 2>&1 || { echo "ERROR: panic-attack is required for this scan" >&2; exit 127; } + panic-attack assail . -# Validate AI installation guide completeness (finishbot pre-release check) -validate-ai-install: +doctor: #!/usr/bin/env bash - echo "=== AI Installation Guide Check ===" - GUIDE="docs/AI_INSTALLATION_GUIDE.adoc" - README="README.adoc" - ERRORS=0 - - # Check guide exists - if [ ! -f "$GUIDE" ]; then - echo "MISSING: $GUIDE (create from template: docs/AI_INSTALLATION_GUIDE.adoc)" - ERRORS=$((ERRORS + 1)) - else - # Check for unfilled TODO markers - TODOS=$(grep -c '\[TODO-AI-INSTALL' "$GUIDE" 2>/dev/null || true) - if [ "$TODOS" -gt 0 ]; then - echo "INCOMPLETE: $GUIDE has $TODOS unfilled [TODO-AI-INSTALL] markers:" - grep -n '\[TODO-AI-INSTALL' "$GUIDE" | head -10 - ERRORS=$((ERRORS + 1)) + set -euo pipefail + missing=0 + for tool in git just idris2 zig; do + if command -v "$tool" >/dev/null 2>&1; then + printf '[OK] %s: %s\n' "$tool" "$(command -v "$tool")" else - echo "$GUIDE: complete (no TODO markers)" - fi - - # Check AI implementation section exists - if ! grep -q 'ai-implementation' "$GUIDE" 2>/dev/null; then - echo "MISSING: [[ai-implementation]] anchor in $GUIDE" - ERRORS=$((ERRORS + 1)) + printf '[MISSING] %s\n' "$tool" >&2 + missing=1 fi + done + printf 'Branch: %s\n' "$(git branch --show-current)" + printf 'Working tree: %s\n' "$(if [ -z "$(git status --porcelain)" ]; then echo clean; else echo modified; fi)" + exit "$missing" - # Check privacy notice exists - if ! grep -qi 'privacy' "$GUIDE" 2>/dev/null; then - echo "MISSING: Privacy notice in $GUIDE" - ERRORS=$((ERRORS + 1)) - fi - - # Check install commands exist (not just placeholders) - if ! grep -q 'git clone' "$GUIDE" 2>/dev/null; then - echo "WARNING: No git clone command found in $GUIDE -- install commands may be incomplete" - fi - fi - - # Check README has AI install section - if [ -f "$README" ]; then - if ! grep -qi 'AI-Assisted Installation' "$README" 2>/dev/null; then - echo "MISSING: AI-Assisted Installation section in $README" - echo " Copy from docs/AI-INSTALL-README-SECTION.adoc" - ERRORS=$((ERRORS + 1)) - fi - - # Check README for unfilled TODO markers - README_TODOS=$(grep -c '\[TODO-AI-INSTALL' "$README" 2>/dev/null || true) - if [ "$README_TODOS" -gt 0 ]; then - echo "INCOMPLETE: $README has $README_TODOS unfilled [TODO-AI-INSTALL] markers" - ERRORS=$((ERRORS + 1)) - fi - fi - - if [ "$ERRORS" -gt 0 ]; then - echo "" - echo "AI install guide: FAIL ($ERRORS issues)" - exit 1 - fi - echo "AI install guide: PASS" - -# Full validation suite -validate: validate-rsr validate-state validate-ai-install - @echo "All validations passed!" - -# ═══════════════════════════════════════════════════════════════════════════════ -# STATE MANAGEMENT -# ═══════════════════════════════════════════════════════════════════════════════ - -# Update STATE.a2ml timestamp -state-touch: - @if [ -f ".machine_readable/STATE.a2ml" ]; then \ - sed -i 's/last-updated = "[^"]*"/last-updated = "'"$(date +%Y-%m-%d)"'"/' .machine_readable/STATE.a2ml && \ - echo "STATE.a2ml timestamp updated"; \ - fi - -# Show current phase from STATE.a2ml -state-phase: - @grep -oP 'phase\s*=\s*"\K[^"]+' .machine_readable/STATE.a2ml 2>/dev/null | head -1 || echo "unknown" - -# ═══════════════════════════════════════════════════════════════════════════════ -# GUIX & NIX -# ═══════════════════════════════════════════════════════════════════════════════ - -# Enter Guix development shell (primary) -guix-shell: - guix shell -D -f build/guix.scm - -# Build with Guix -guix-build: - guix build -f build/guix.scm - -# Enter Nix development shell (fallback) -nix-shell: - @if [ -f "flake.nix" ]; then nix develop; else echo "No flake.nix"; fi - -# ═══════════════════════════════════════════════════════════════════════════════ -# HYBRID AUTOMATION -# ═══════════════════════════════════════════════════════════════════════════════ - -# Run local automation tasks -automate task="all": - #!/usr/bin/env bash - case "{{task}}" in - all) just fmt && just lint && just test && just docs && just state-touch ;; - cleanup) just clean && find . -name "*.orig" -delete && find . -name "*~" -delete ;; - update) just deps && just validate ;; - *) echo "Unknown: {{task}}. Use: all, cleanup, update" && exit 1 ;; - esac - -# ═══════════════════════════════════════════════════════════════════════════════ -# COMBINATORIC MATRIX RECIPES -# ═══════════════════════════════════════════════════════════════════════════════ - -# Build matrix: [debug|release] x [target] x [features] -build-matrix mode="debug" target="" features="": - @echo "Build matrix: mode={{mode}} target={{target}} features={{features}}" - -# Test matrix: [unit|integration|e2e|all] x [verbosity] x [parallel] -test-matrix suite="unit" verbosity="normal" parallel="true": - @echo "Test matrix: suite={{suite}} verbosity={{verbosity}} parallel={{parallel}}" - -# Container matrix: [build|run|push|shell|scan] x [registry] x [tag] -container-matrix action="build" registry="ghcr.io/hyperpolymath" tag="latest": - @echo "Container matrix: action={{action}} registry={{registry}} tag={{tag}}" - -# CI matrix: [lint|test|build|security|all] x [quick|full] -ci-matrix stage="all" depth="quick": - @echo "CI matrix: stage={{stage}} depth={{depth}}" - -# Show all matrix combinations -combinations: - @echo "=== Combinatoric Matrix Recipes ===" - @echo "" - @echo "Build Matrix: just build-matrix [debug|release] [target] [features]" - @echo "Test Matrix: just test-matrix [unit|integration|e2e|all] [verbosity] [parallel]" - @echo "Container: just container-matrix [build|run|push|shell|scan] [registry] [tag]" - @echo "CI Matrix: just ci-matrix [lint|test|build|security|all] [quick|full]" - -# ═══════════════════════════════════════════════════════════════════════════════ -# VERSION CONTROL -# ═══════════════════════════════════════════════════════════════════════════════ - -# Show git status status: @git status --short -# Show recent commits log count="20": @git log --oneline -{{count}} -# Generate CHANGELOG.md with git-cliff -changelog: - @command -v git-cliff >/dev/null || { echo "git-cliff not found — install: cargo install git-cliff"; exit 1; } - git cliff --output CHANGELOG.md - @echo "Generated CHANGELOG.md" - -# Preview changelog for unreleased commits (does not write) -changelog-preview: - @command -v git-cliff >/dev/null || { echo "git-cliff not found — install: cargo install git-cliff"; exit 1; } - git cliff --unreleased --strip header - -# Tag a new release (usage: just release-tag 1.2.3) -release-tag version: - #!/usr/bin/env bash - TAG="v{{version}}" - if git rev-parse "$TAG" >/dev/null 2>&1; then - echo "Tag $TAG already exists" - exit 1 - fi - just changelog - git add CHANGELOG.md - git commit -m "chore(release): prepare $TAG" - git tag -a "$TAG" -m "Release $TAG" - echo "Created tag $TAG — push with: git push origin main --tags" - -# ═══════════════════════════════════════════════════════════════════════════════ -# UTILITIES -# ═══════════════════════════════════════════════════════════════════════════════ - -# Count lines of code -loc: - @find . \( -name "*.rs" -o -name "*.ex" -o -name "*.exs" -o -name "*.res" -o -name "*.gleam" -o -name "*.zig" -o -name "*.idr" -o -name "*.hs" -o -name "*.ncl" -o -name "*.scm" -o -name "*.adb" -o -name "*.ads" \) -not -path './target/*' -not -path './_build/*' 2>/dev/null | xargs wc -l 2>/dev/null | tail -1 || echo "0" - -# Show TODO comments -todos: - @grep -rn "TODO\|FIXME\|HACK\|XXX" --include="*.rs" --include="*.ex" --include="*.res" --include="*.gleam" --include="*.zig" --include="*.idr" --include="*.hs" . 2>/dev/null || echo "No TODOs" - -# Open in editor -edit: - ${EDITOR:-code} . - -# Run panic-attacker pre-commit scan (whole repo) -assail: - @command -v panic-attack >/dev/null 2>&1 && panic-attack assail . || echo "panic-attack not found — install from https://github.com/hyperpolymath/panic-attacker" - -# Run panic-attack unified-api-adapter surface scan on the nesy-solver-api V connector. -# Uses connectors/proven-nesy-solver-api/v/panic-attack.toml for surface-specific -# thresholds (REST + VerisimDB held to 85 robustness; others to 75). -assail-unified-api-adapter: - @command -v panic-attack >/dev/null 2>&1 || { echo "panic-attack not found — install from https://github.com/hyperpolymath/panic-attacker"; exit 1; } - @echo "Scanning unified-api-adapter connector (16 surfaces)..." - @cd connectors/proven-nesy-solver-api/v && panic-attack assail . --config panic-attack.toml - -# Self-diagnostic — checks dependencies, permissions, paths -doctor: - @echo "Running diagnostics for proven-servers..." - @echo "Checking required tools..." - @command -v just >/dev/null 2>&1 && echo " [OK] just" || echo " [FAIL] just not found" - @command -v git >/dev/null 2>&1 && echo " [OK] git" || echo " [FAIL] git not found" - @echo "Checking for hardcoded paths..." - @grep -rn '$HOME\|$ECLIPSE_DIR' --include='*.rs' --include='*.ex' --include='*.res' --include='*.gleam' --include='*.sh' . 2>/dev/null | head -5 || echo " [OK] No hardcoded paths" - @echo "Diagnostics complete." - -# Auto-repair common issues -heal: - @echo "Attempting auto-repair for proven-servers..." - @echo "Fixing permissions..." - @find . -name "*.sh" -exec chmod +x {} \; 2>/dev/null || true - @echo "Cleaning stale caches..." - @rm -rf .cache/stale 2>/dev/null || true - @echo "Repair complete." - -# Guided tour of key features tour: - @echo "=== proven-servers Tour ===" - @echo "" - @echo "1. Project structure:" - @ls -la - @echo "" - @echo "2. Available commands: just --list" - @echo "" - @echo "3. Read README.adoc for full overview" - @echo "4. Read EXPLAINME.adoc for architecture decisions" - @echo "5. Run 'just doctor' to check your setup" - @echo "" - @echo "Tour complete! Try 'just --list' to see all available commands." - -# Open feedback channel with diagnostic context + @echo "Read README.adoc, QUICKSTART-DEV.adoc, and the package-local README before building a component." + help-me: - @echo "=== proven-servers Help ===" - @echo "Platform: $(uname -s) $(uname -m)" - @echo "Shell: $SHELL" - @echo "" - @echo "To report an issue:" - @echo " https://github.com/hyperpolymath/proven-servers/issues/new" - @echo "" - @echo "Include the output of 'just doctor' in your report." + @echo "Issues: https://github.com/hyperpolymath/proven-servers/issues/new" + @echo "Include the exact package, commit, tool versions, command, and complete output." +todos: + @git grep -n -E 'TODO|FIXME|XXX|HACK|STUB|PARTIAL' -- ':!PLACEHOLDERS.adoc' || true -# Print the current CRG grade (reads from READINESS.md '**Current Grade:** X' line) crg-grade: - @grade=$$(grep -oP '(?<=\*\*Current Grade:\*\* )[A-FX]' READINESS.md 2>/dev/null | head -1); \ - [ -z "$$grade" ] && grade="X"; \ - echo "$$grade" + @sed -n 's/^\*\*Current Grade:\*\* \([A-FX]\).*/\1/p' READINESS.adoc | head -1 -# Generate a shields.io badge markdown for the current CRG grade -# Looks for '**Current Grade:** X' in READINESS.md; falls back to X crg-badge: - @grade=$$(grep -oP '(?<=\*\*Current Grade:\*\* )[A-FX]' READINESS.md 2>/dev/null | head -1); \ - [ -z "$$grade" ] && grade="X"; \ - case "$$grade" in \ - A) color="brightgreen" ;; B) color="green" ;; C) color="yellow" ;; \ - D) color="orange" ;; E) color="red" ;; F) color="critical" ;; \ - *) color="lightgrey" ;; esac; \ - echo "[![CRG $$grade](https://img.shields.io/badge/CRG-$$grade-$$color?style=flat-square)](https://github.com/hyperpolymath/standards/tree/main/component-readiness-grades)" - -secret-scan-trufflehog: - @command -v trufflehog >/dev/null && trufflehog filesystem . --only-verified || true + #!/usr/bin/env bash + set -euo pipefail + grade="$(sed -n 's/^\*\*Current Grade:\*\* \([A-FX]\).*/\1/p' READINESS.adoc | head -1)" + [ -n "$grade" ] || grade=X + case "$grade" in + A) color=brightgreen ;; B) color=green ;; C) color=yellow ;; + D) color=orange ;; E) color=red ;; F) color=critical ;; + *) color=lightgrey ;; + esac + printf '[![CRG %s](https://img.shields.io/badge/CRG-%s-%s?style=flat-square)](https://github.com/hyperpolymath/standards/tree/main/component-readiness-grades)\n' "$grade" "$grade" "$color" + +# This source tree is not a deployable service: refuse container operations +# until a real executable target, health endpoint, and image tests are added. +container-build container-verify container-up container-down container-sign container-push container-run: + @echo "ERROR: container deployment is disabled; this repository has no deployable server binary." >&2 + @exit 2 diff --git a/MAINTAINERS.adoc b/MAINTAINERS.adoc deleted file mode 100644 index aa23a55d..00000000 --- a/MAINTAINERS.adoc +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Maintainers -:toc: preamble - -This document lists the maintainers of this project and their responsibilities. - -== Current Maintainers - -[cols="2,3,2",options="header"] -|=== -| Name | Role | Contact - -| Jonathan D.A. Jewell -| Lead Maintainer -| https://github.com/hyperpolymath[@hyperpolymath] -|=== - -== Responsibilities - -Maintainers are responsible for: - -* Reviewing and merging pull requests -* Triaging issues and feature requests -* Ensuring code quality and security standards -* Managing releases and versioning -* Upholding the project's code of conduct - -== Becoming a Maintainer - -Contributors who demonstrate: - -* Consistent, high-quality contributions -* Understanding of the project's goals and standards -* Constructive participation in discussions -* Commitment to the project's long-term health - -May be invited to become maintainers at the discretion of existing maintainers. - -== Decision Making - -* Routine decisions (bug fixes, minor improvements) can be made by any maintainer -* Significant changes require discussion and consensus among maintainers -* Breaking changes or major features should be discussed in issues before implementation - -== Contact - -For questions about project governance, open an issue or contact the maintainers listed above. diff --git a/PLACEHOLDERS.adoc b/PLACEHOLDERS.adoc index 96aeb32e..6afc620d 100644 --- a/PLACEHOLDERS.adoc +++ b/PLACEHOLDERS.adoc @@ -1,219 +1,42 @@ -== Template Placeholders +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += Placeholder and Template-Residue Note +:toc: -All placeholders in this template follow the `+{{PLACEHOLDER}}+` -pattern. After cloning, replace them with your project-specific values. +This repository is a project-specific source monorepo, not a bootstrap template. +Its maintainer/contact metadata is intentional. Do not run a repository-wide +search-and-replace against author names, project identifiers, dates, or URLs. -=== Recommended: Interactive Bootstrap +[IMPORTANT] +==== +There is no `just init` or `just validate-ai-install` task in the root +`Justfile`. The old instructions in this file referred to template automation +that is not present here. Do not use the historical `sed -i` examples: they +were broad, destructive replacements and included a no-op `github.com` rewrite. +==== -[source,bash] ----- -just init ----- - -This interactively prompts for all values, replaces every placeholder, -validates the result, and runs k9-svc checks if available. +== Safe review -=== Manual Replace +Search for an exact marker before making a scoped, reviewed change: [source,bash] ---- -# If you prefer manual replacement (run from repo root) - -sed -i 's/Jonathan D.A. Jewell/Jane Doe/g' $(grep -rl 'Jonathan D.A. Jewell' .) -sed -i 's/j.d.a.jewell@open.ac.uk/jane@example.org/g' $(grep -rl 'j.d.a.jewell@open.ac.uk' .) -sed -i 's/hyperpolymath/my-org/g' $(grep -rl 'hyperpolymath' .) -sed -i 's/Proven Servers/my-project/g' $(grep -rl 'Proven Servers' .) -sed -i 's/{{PROJECT}}/MY_PROJECT/g' $(grep -rl '{{PROJECT}}' .) -sed -i 's/{{project}}/my_project/g' $(grep -rl '{{project}}' .) -sed -i 's/proven-servers/my-project/g' $(grep -rl 'proven-servers' .) -sed -i 's/github.com/github.com/g' $(grep -rl 'github.com' .) -sed -i "s/2026/$(date +%Y)/g" $(grep -rl '2026' .) -sed -i "s/2026-03-16/$(date +%Y-%m-%d)/g" $(grep -rl '2026-03-16' .) +rg -n '\{\{[^}]+\}\}|\[TODO-AI-INSTALL\]|YOUR[-_ ](NAME|PROJECT|REPO)' \ + --glob '!PLACEHOLDERS.adoc' --glob '!*.lock' . ---- -=== Placeholder Reference - -==== Author & Copyright - -[width="100%",cols="25%,25%,25%,25%",options="header",] -|=== -|Placeholder |Description |Example |Files -|`+Jonathan D.A. Jewell+` |Full legal name |`+Jane Doe+` |SPDX headers -(all files), MAINTAINERS.md, .mailmap, .reuse/dep5, -docs/AI-CONVENTIONS.md - -|`+j.d.a.jewell@open.ac.uk+` |Primary contact email -|`+jane@example.org+` |SPDX headers (all files), .mailmap, .reuse/dep5, -.well-known/humans.txt - -|`+{{AUTHOR_EMAIL_ALT}}+` |Previous/secondary email (for .mailmap) -|`+old@example.com+` |.mailmap - -|`+{{AUTHOR_ORG}}+` |Author’s organization/affiliation -|`+Acme University+` |project-metadata.k9.ncl - -|`+{{AUTHOR_LAST}}+` |Author surname (for citations) |`+Doe+` -|docs/CITATIONS.adoc - -|`+{{AUTHOR_FIRST}}+` |Author first name (for citations) |`+Jane+` -|docs/CITATIONS.adoc - -|`+{{AUTHOR_INITIALS}}+` |Author initials (for citations) |`+J.+` -|docs/CITATIONS.adoc -|=== - -==== Project Identity - -[width="100%",cols="25%,25%,25%,25%",options="header",] -|=== -|Placeholder |Description |Example |Files -|`+Proven Servers+` |Human-readable project name |`+My Project+` -|SECURITY.md, CODE_OF_CONDUCT.md, TOPOLOGY.md, STATE.a2ml, Justfile, -GOVERNANCE.md, MAINTAINERS.md, flake.nix, devcontainer.json - -|`+{{PROJECT_DESCRIPTION}}+` |One-line description |`+A tool for X+` -|flake.nix - -|`+{{PROJECT}}+` |Uppercase identifier (for Idris2 modules, C macros) -|`+MY_PROJECT+` |ABI-FFI-README.md, src/abi/_.idr, ffi/zig/_.zig - -|`+{{project}}+` |Lowercase identifier (for C symbols, filenames) -|`+my_project+` |ABI-FFI-README.md, ffi/zig/*.zig - -|`+proven-servers+` |Repository name (slug) |`+my-project+` -|CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md, cliff.toml - -|`+hyperpolymath+` |GitHub/GitLab org or username |`+my-org+` |SPDX -headers, CONTRIBUTING.md, SECURITY.md, GOVERNANCE.md, MAINTAINERS.md, -CODEOWNERS, mirror.yml, cliff.toml - -|`+github.com+` |Git forge domain |`+github.com+` |CONTRIBUTING.md -|=== - -==== Dates - -[width="100%",cols="25%,25%,25%,25%",options="header",] -|=== -|Placeholder |Description |Example |Files -|`+2026+` |Current year |`+2026+` |SPDX headers (all files), -GOVERNANCE.md, MAINTAINERS.md - -|`+2026-03-16+` |Current date (ISO) |`+2026-02-14+` |STATE.a2ml, -MAINTAINERS.md - -|`+{{DATE}}+` |Last updated date |`+2026-02-14+` |TOPOLOGY.md, -THREAT-MODEL.md -|=== - -==== Contact & Security - -[width="100%",cols="25%,25%,25%,25%",options="header",] -|=== -|Placeholder |Description |Example |Files -|`+6759885+hyperpolymath@users.noreply.github.com+` |Security contact -email |`+security@example.org+` |SECURITY.md +A match is not automatically a defect: documentation may discuss marker syntax, +and disabled or experimental scaffolding may preserve an explicit placeholder. +Review each match in context. Never modify generated files, source, metadata, +or licensing text with a global substitution. -|`+[PGP fingerprint not set]+` |40-char PGP fingerprint -|`+ABCD 1234 ...+` |SECURITY.md +== Current project guidance -|`+{{PGP_KEY_URL}}+` |URL to public PGP key -|`+https://keys.openpgp.org/...+` |SECURITY.md - -|`+{{WEBSITE}}+` |Project website |`+https://example.org+` |SECURITY.md - -|`+{{CONDUCT_EMAIL}}+` |Conduct reports email |`+conduct@example.org+` -|CODE_OF_CONDUCT.md - -|`+{{CONDUCT_TEAM}}+` |Conduct committee name -|`+Code of Conduct Committee+` |CODE_OF_CONDUCT.md - -|`+{{RESPONSE_TIME}}+` |SLA for initial response |`+48 hours+` -|CODE_OF_CONDUCT.md -|=== - -==== Git - -[cols=",,,",options="header",] -|=== -|Placeholder |Description |Example |Files -|`+main+` |Main branch name |`+main+` |CONTRIBUTING.md -|=== - -==== Build - -[width="100%",cols="25%,25%,25%,25%",options="header",] -|=== -|Placeholder |Description |Example |Files -|`+MPL-2.0+` |License name |`+MPL-2.0+` |ABI-FFI-README.md - -|`+{{PROJECT_PURPOSE}}+` |One-line project description -|`+FFI bridges between languages+` |STATE.a2ml -|=== - -==== AI Manifest - -[width="100%",cols="25%,25%,25%,25%",options="header",] -|=== -|Placeholder |Description |Example |Files -|`+[YOUR-REPO-NAME]+` |Repository name |`+my-project+` -|0-AI-MANIFEST.a2ml - -|`+[DATE]+` |Creation date |`+2026-02-14+` |0-AI-MANIFEST.a2ml - -|`+[YOUR-NAME/ORG]+` |Maintainer name |`+hyperpolymath+` -|0-AI-MANIFEST.a2ml -|=== - -==== AI Installation Guide - -[width="100%",cols="34%,33%,33%",options="header",] -|=== -|Marker |Description |Files -|`+[TODO-AI-INSTALL]+` |Unfilled section in AI installation guide -|`+docs/AI_INSTALLATION_GUIDE.adoc+`, -`+docs/AI-INSTALL-README-SECTION.adoc+`, `+README.adoc+` -|=== - -These are *not* standard `+{{PLACEHOLDER}}+` markers – they are TODO -markers that must be replaced with project-specific content before -release. They mark sections where the developer (or AI) must fill in: - -* What questions the AI should ask the user -* Exact prerequisite check and install commands -* Privacy notice specific to this project -* Complete installation command block -* Credential setup instructions (URLs, scopes, env vars) -* Verification commands and expected output -* Error handling table -* Example conversation - -*finishbot checks:* `+just validate-ai-install+` verifies no -`+[TODO-AI-INSTALL]+` markers remain. - -=== Deletion Markers - -Some files contain deletion instructions: - -[width="100%",cols="34%,33%,33%",options="header",] -|=== -|Marker |Meaning |File -|`+{{~ ... ~}}+` |Delete this entire line after reading -|ABI-FFI-README.md (line 1) -|=== - -=== Verification - -After replacing all placeholders, verify none remain: - -[source,bash] ----- -grep -rn '{{' . --include='*.md' --include='*.adoc' --include='*.a2ml' \ - --include='*.scm' --include='*.idr' --include='*.zig' --include='*.res' \ - --include='Justfile' --include='*.nix' --include='*.toml' --include='*.yml' \ - --include='*.yaml' --include='*.hs' --include='*.ncl' --include='*.txt' \ - --include='*.json' --include='Containerfile' --include='dep5' \ - | grep -v 'PLACEHOLDERS.md' | grep -v 'node_modules' ----- +* Use `just --list` to see actual tasks; consult `Justfile` before running one. +* Follow `README.adoc` and `QUICKSTART-DEV.adoc` for package-scoped checks. +* Follow `.machine_readable/6a2/STATE.a2ml` for the current evidence boundary. +* Update real contact, licensing, or project metadata only in a scoped change. -If the above command produces no output, all placeholders have been -replaced. +This file intentionally contains examples of marker syntax and is excluded +from the command above. Its existence is not a claim that unresolved template +placeholders remain elsewhere in the repository. diff --git a/PROOF-NEEDS.adoc b/PROOF-NEEDS.adoc index fba1acf7..e0f78829 100644 --- a/PROOF-NEEDS.adoc +++ b/PROOF-NEEDS.adoc @@ -1,54 +1,106 @@ -== PROOF-NEEDS.md — proven-servers +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += Proof and Verification Needs — proven-servers -=== Current State +:revdate: 2026-09-27 +:toc: -* **src/abi/*.idr**: YES — `+Types.idr+`, `+Layout.idr+`, -`+Foreign.idr+` -* *Dangerous patterns*: 0 in own code (1 reference in NeSy/Types.idr is -a documentation comment about neurosymbolic equivalent of believe_me) -* *LOC*: ~21,700 (Idris2 + Rust + Gleam + Go + Haskell) -* *ABI layer*: Complete Idris2 ABI; proven-dns and proven-nesy protocol -definitions in Idris2 +== Current answer -=== What Needs Proving +There is no current repository-wide proof or test claim established by this +assessment. The source tree contains Idris2 models, Zig implementations, C headers and ABI +artefacts with varying generation status, and language-binding directories, +but their presence does not establish that they compile or conform to one +another. Current build +status is *unknown*: Idris2 and Zig were not available in the audit workspace. -[width="100%",cols="51%,27%,22%",options="header",] +The `audits/` directory contains older, date-stamped reports. Treat them as +historical evidence for the revisions they examined, not as verification of the +current working tree. + +== Verification boundary + +A successful Idris2 totality/type-checking run validates the theorem statements +and definitions included in the specific package build. It does not prove that +an independently written Zig FFI implements those definitions, that a C header +matches both, or that an external network protocol is implemented correctly. +Likewise, a Zig test suite only supports the behaviors it actually executes; it +does not establish general RFC compliance, cryptographic correctness, or +production suitability. + +The shell scripts in `tests/` primarily perform source-pattern checks and +partial package sweeps. They must not be described as exhaustive property tests, +formal proofs, full cross-language conformance tests, or security certification. + +== Priority verification work + +[cols="1,3,3"] |=== -|Component |What |Why -|DNS name validation |DNS name parser accepts only valid names per RFC -|Invalid DNS names cause resolution failures +|Priority |Required evidence |Why it matters -|DNS protocol correctness |DNS query/response handling is correct |Wrong -DNS responses break name resolution +|1 — Reproducible builds +|Build each tracked Idris2 `.ipkg` and Zig `build.zig` with pinned toolchain +versions. Record the exact commit, versions, commands, and outcomes; make every +failure visible. +|A type-correct model or source file is not build evidence for the current +revision. -|NeSy type safety |Neurosymbolic type system is sound |NeSy Types.idr -defines the neurosymbolic bridge — must be correct +|2 — ABI/FFI correspondence +|For each claimed bridge, regenerate or verify headers from the ABI source, +check tags/layouts/symbol signatures, and execute boundary tests against the +compiled native library. +|The Idris model and separately maintained Zig/C implementation can diverge. -|Rust FFI bindings |All 9 FFI modules (dns, firewall, ftp, graphql, -grpc, httpd, mqtt, smtp, ssh) correctly implement Idris2 ABI |FFI -boundary bugs defeat proven guarantees +|3 — Fail-closed behavior +|Add executable negative tests for unavailable crypto, authentication, +policy, storage, and backend operations; verify output buffers, status codes, +and state are reset on rejection. +|A function that rejects safely must not be documented as successful +functionality or an operational security control. -|Server protocol compliance |Each protocol server (httpd, smtp, ssh, -etc.) adheres to its RFC |Protocol violations cause interoperability -failures +|4 — Language bindings +|Compile, link, and run each binding that is claimed as supported against the +exact library and ABI. Record unimplemented JNI/NIF/C-stub bridges as +unavailable rather than wired. +|A directory of declarations can compile independently while failing at link +or runtime. -|Firewall rule evaluation |Firewall rules evaluate correctly and -completely |Missed rules create security gaps +|5 — Protocol/security behavior +|Use authoritative protocol test vectors, malformed-input cases, fuzzing, and +external interoperability/security review for any implementation intended for +real traffic. +|Model-level state transitions do not supply a complete parser, transport, +cryptographic backend, or server. +|=== + +== Current evidence ledger -|NeSy believe_me-equivalent tracking |Track and minimize neurosymbolic -escape hatches |The documented "`believe_me equivalent`" must be -minimized +[cols="2,2,3"] |=== +|Question |Current status |Next evidence -=== Recommended Prover +|Do all Idris2 packages build on this revision? +|Unknown; compiler unavailable in the audit environment. +|Run `just build-idris` with a pinned Idris2 release and record results. -*Idris2* — ABI layer complete. DNS and NeSy protocol proofs are natural -extensions. Server protocol RFC compliance could use *Lean4* for the -specification-level proofs. +|Do all Zig FFI packages build and pass tests on this revision? +|Unknown; compiler unavailable in the audit environment. +|Run `just build-zig` and `just test-zig` using supported package-specific Zig +versions; the repository does not yet define a complete version matrix. -=== Priority +|Are all language bindings operational? +|No such claim is supported. The OCaml tree is explicitly disabled; Java and +Kotlin declarations require their JNI implementation; other source wrappers +also require package-specific link/runtime verification. +|Maintain `.machine_readable/BINDINGS.a2ml` and revalidate each claimed binding. -*HIGH* — Server protocol implementations with security implications -(firewall, SSH, SMTP, DNS). Incorrect protocol handling is a security -vulnerability. The 9 Rust FFI modules are the critical boundary where -proven guarantees could be lost. +|Are the current shell smoke checks formal properties? +|No. They inspect source patterns and sample paths. +|Keep their scope explicit; replace them with executable tests and generated +conformance checks where a property is claimed. + +|Are production services or cryptographic backends present? +|Not established. Some operations explicitly fail closed. +|Implement and independently audit concrete backends before making service, +security, or cryptographic claims. +|=== diff --git a/QUICKSTART-DEV.adoc b/QUICKSTART-DEV.adoc index 0f7d39e6..17d1f98e 100644 --- a/QUICKSTART-DEV.adoc +++ b/QUICKSTART-DEV.adoc @@ -1,139 +1,93 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -// QUICKSTART-DEV.adoc — clone → build → test → PR -= proven-servers — Quick Start for Developers += proven-servers — Developer Quick Start + +:revdate: 2026-09-27 :toc: :toclevels: 2 -== Tech Stack - -* *ABI + proofs*: Idris 2 (dependent types; `%default total`). Each component's - ABI lives in `src/ABI/` — tag encodings with round-trip proofs, - state-machine GADTs, and the FFI contract. Type-checking *is* proof checking. -* *FFI / engines*: Zig 0.15+ in `ffi/zig/`, exporting a C ABI. -* *Bridge*: generated C headers in `generated/abi/`. -* *Bindings*: 20 languages in `bindings/` — Ada, C++, C#, Dart, Elixir, Gleam, - Go, Haskell, Java, JavaScript, Julia, Kotlin, Lua, OCaml, PHP, Python, ReScript, - Ruby, Rust, Swift — thin wrappers over the same C ABI (Elixir/Gleam/ReScript are - constants-only scaffolds pending FFI; see ADR 0003). - -== Set Up Development Environment +[IMPORTANT] +==== +This repository is a collection of protocol models, FFI prototypes, and binding +scaffolds. It is not a production server suite. A successful model build does +not prove that a native implementation or language wrapper conforms to it. +==== -=== Option A: Guix (preferred) - -[source,bash] ----- -guix shell ----- +== Toolchains -=== Option B: Nix (fallback) +The source tree uses Idris2 for model packages and Zig for native FFI packages. +A Zig `build.zig` or Idris `.ipkg` is package-specific: not every package +contains both. The current workspace used for this status update did not have +Idris2, Zig, or Just installed, so its modified compiler sources have not been +validated here. -[source,bash] ----- -nix develop ----- +Install the toolchains required by the package you plan to work on. Check the +package's README, `.ipkg`, and `build.zig` for its actual dependencies and +supported compiler versions. The root `mise.toml` pins Just only; supported +Idris2 and Zig versions and the full package matrix are not yet reproducibly +pinned. -=== Option C: Manual +== Build and test one package [source,bash] ---- -git clone https://github.com/hyperpolymath/proven-servers.git -cd proven-servers -just setup-dev ----- - -== Build +# Idris2 model (example package) +(cd protocols/proven-dns && idris2 --build proven-dns.ipkg) -[source,bash] +# Zig implementation and tests (same example) +(cd protocols/proven-dns/ffi/zig && zig build) +(cd protocols/proven-dns/ffi/zig && zig build test) ---- -# Idris2 core + proofs for one component (a clean build = proofs verified): -idris2 --build protocols/proven-/proven-.ipkg -# Zig FFI engine (shared + static libraries): -(cd protocols/proven-/ffi/zig && zig build) +Use the package directory's documentation for the scope and limitations of +those checks. A successful `idris2 --build` validates only the model modules +included by that package manifest. A Zig test result is evidence only for the +code paths that the test executes. -# Or, via the task runner: -just build ----- +== Repository tasks -== Test +The `Justfile` is the task entry point: [source,bash] ---- -# One engine's FFI tests: -(cd protocols/proven-/ffi/zig && zig build test) - -# Full end-to-end suite (FFI builds + safety aspects): -bash tests/e2e.sh # or: just test +just build-idris # Build discovered Idris2 packages; requires idris2 +just build-zig # Build discovered Zig packages; requires zig +just test-zig # Run discovered Zig package tests; requires zig +just test-static # Run source-pattern smoke checks and binding inventory check +just build # Aggregate native/model build; fails if tools are absent +just test # Aggregate build and test checks; fails if tools are absent +just e2e # Selected package build/test sweep; requires Idris2 and Zig +just validate-rsr # Validate the actual repository metadata paths ---- -== Project Structure +These commands are intended to run work, not print success without checking +anything. Review the `Justfile` implementation and exit status. Static smoke +checks are not substitutes for native builds, executable tests, or formal +proofs. `just test-static` intentionally has narrower claims than `just test`. + +== Repository map [source] ---- proven-servers/ -├── src/ # Source code -├── src/abi/ # Idris2 ABI definitions (if applicable) -├── ffi/zig/ # Zig FFI bridge (if applicable) -├── tests/ # Test suite -├── docs/ # Documentation -├── .machine_readable/ # Checkpoint files (STATE, META, ECOSYSTEM) -├── Justfile # Task runner recipes -├── guix.scm # Guix environment -├── flake.nix # Nix environment (fallback) -└── 0-AI-MANIFEST.a2ml # AI agent entry point ----- - -== Key Recipes - -[source,bash] ----- -just build # Build the project -just test # Run tests -just doctor # Self-diagnostic -just lint # Lint and format -just panic-scan # Security scan via panic-attacker -just tour # Guided tour of the codebase ----- - -== Before Submitting a PR - -[source,bash] ----- -just lint # Format and lint -just test # All tests pass -just panic-scan # No new security issues ----- - -== Contractile Invariants - -Read `.machine_readable/MUST.contractile` before making changes. -Key invariants that must never be violated: - -* The ABI is defined in Idris 2; FFIs/APIs are implemented in Zig (the RSR - standard — see `ABI-FFI-README.md`). -* No proof-escape hatches in Idris code (`believe_me`, `assert_total`, - `assert_smaller`, `idris_crash`, `postulate`); `%default total` in every module. -* No `@panic` or `unreachable` in Zig FFI production code; every exported - function is NULL-/invalid-handle-safe. -* ABI tag values must match exactly between Idris (`*ABI.Types`) and Zig - (`@intFromEnum`); the FFI tests enforce this. -* Every component's ABI modules are listed in its `.ipkg`, so the proofs are - actually compiled (not orphaned). - -== LLM/AI Agent Development - -If using an AI assistant, load the warmup context first: - -[source,bash] ----- -just llm-context # Outputs role-appropriate context ----- - -Or read `0-AI-MANIFEST.a2ml` and `.claude/CLAUDE.md` directly. - -== Get Help - -* **Architecture**: link:EXPLAINME.adoc[EXPLAINME.adoc] -* **Wiki**: https://github.com/hyperpolymath/proven-servers/wiki -* **Report issue**: `just help-me` +├── protocols/ # Protocol packages; contents and readiness vary +├── core/ # Shared packages +├── connectors/ # Connector/integration packages +├── bindings/ # Language-specific wrapper sources and scaffolds +├── not-proven/ # Explicitly non-proven material +├── tests/ # Partial runtime suites and source-pattern checks +├── audits/ # Historical audit snapshots +├── .machine_readable/ # State, maintenance policy, registries, guidance +└── Justfile # Root task definitions +---- + +== Before submitting changes + +. Read `0-AI-MANIFEST.a2ml`, `.machine_readable/6a2/STATE.a2ml`, and + `.machine_readable/6a2/AGENTIC.a2ml`. +. Inspect the package README and current test scope. +. Run the relevant package build and tests with recorded toolchain versions. +. Run `git diff --check`, `just test-static`, and the package-specific checks. +. Update readiness and binding claims only when new evidence has actually been + reproduced. +. Describe limitations and remaining verification work in the pull request. diff --git a/QUICKSTART-MAINTAINER.adoc b/QUICKSTART-MAINTAINER.adoc index 27627862..2c0f2a3a 100644 --- a/QUICKSTART-MAINTAINER.adoc +++ b/QUICKSTART-MAINTAINER.adoc @@ -1,130 +1,82 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -// Template: QUICKSTART-MAINTAINER.adoc — packaging, deploying, and maintaining -// Replace proven-servers, {{PACKAGE_NAME}}, {{DEPS}} with actuals -= proven-servers — Quick Start for Platform Maintainers += proven-servers — Maintainer Quick Start :toc: -:toclevels: 2 +:sectnums: -== Overview +[IMPORTANT] +==== +This repository is a research/prototype source monorepo. It has no configured +release artifact, system package, production container image, or validated +network-service deployment. This guide is for maintaining source and evidence, +not for packaging or deploying a server. +==== -This guide covers packaging, deploying, and maintaining proven-servers for -distribution on your platform. - -== Runtime Dependencies - -{{DEPS}} - -== Build from Source - -[source,bash] ----- -git clone https://github.com/hyperpolymath/proven-servers.git -cd proven-servers -just build-release ----- - -Output: `{{BUILD_OUTPUT_PATH}}` - -== Packaging - -=== Guix - -[source,bash] ----- -guix build -f build/guix.scm ----- - -=== Nix - -[source,bash] ----- -nix build ----- - -=== Container (Stapeln) - -[source,bash] ----- -just stapeln-export # Generates Containerfile -podman build -t proven-servers . ----- - -=== Manual Package - -[source,bash] ----- -just install --prefix=/usr/local ----- - -Files installed: - -[cols="1,2"] -|=== -| Path | Contents - -| `$PREFIX/bin/` -| Executables - -| `$PREFIX/share/{{PACKAGE_NAME}}/` -| Data files, assets - -| `$PREFIX/share/doc/{{PACKAGE_NAME}}/` -| Documentation - -| `$PREFIX/share/applications/` -| .desktop file (Linux, if GUI) - -| `$PREFIX/share/man/man1/` -| Man pages -|=== - -== Configuration - -Default config location: `$XDG_CONFIG_HOME/{{PACKAGE_NAME}}/config.toml` - -Fallback: `$HOME/.config/{{PACKAGE_NAME}}/config.toml` - -== Health Checks +== Establish current state [source,bash] ---- -just doctor # Full diagnostic -just run --version # Version check -just run --selftest # Built-in self-test +git status --short +git log -1 --oneline +just info +just deps ---- -== Updating - -[source,bash] ----- -git pull -just build-release -just install --prefix=/usr/local ----- - -Or via OPSM: `opsm update {{PACKAGE_NAME}}` - -== Security Notes +`just deps` reports whether Just, Idris2, and Zig are available. It does not +install them. `mise.toml` pins Just only; Idris2 and Zig are not pinned. Inspect each +package manifest and build file before selecting compiler versions. -* License: MPL-2.0 (Palimpsest License) -* All dependencies SHA-pinned -* `panic-attacker` scan results: link:INSTALL-SECURITY-REPORT.adoc[] -* OpenSSF Scorecard: see badge in README - -== Multi-Instance Deployment - -For deploying multiple instances (e.g., different users or tenants): +== Verification sequence [source,bash] ---- -just install --prefix=/opt/{{PACKAGE_NAME}}-instance1 --config=/etc/{{PACKAGE_NAME}}/instance1.toml -just install --prefix=/opt/{{PACKAGE_NAME}}-instance2 --config=/etc/{{PACKAGE_NAME}}/instance2.toml ----- - -Each instance has isolated config, data, and logs. - -== Reporting Issues - -* Upstream: https://github.com/hyperpolymath/proven-servers/issues -* With diagnostic: `just help-me` (pre-fills context) +just fmt-check +just lint +just test-static +just build-idris +just build-zig +just test-zig +just test +just validate-rsr +---- + +The compiler-backed commands fail if required tools are absent. `test-static` +contains source-pattern/inventory checks only. `validate-rsr` checks selected +metadata paths and the synchronized Justfile snapshot; it is not a full RSR +certification. Record the commit, tool versions, exact commands, and results. + +For focused work, run the package-specific `.ipkg` or `zig build` test target +and read its README. A package build does not establish external protocol +conformance, repository-wide ABI equivalence, production suitability, or +formal correctness beyond the checked propositions. + +== Evidence and change review + +. Read `README.adoc`, `READINESS.adoc`, `PROOF-NEEDS.adoc`, and + `.machine_readable/6a2/STATE.a2ml`. +. Inspect the implementation and test coverage; treat old audit reports as + historical evidence only. +. Keep binding availability, proof status, cryptographic claims, and protocol + support aligned with current executable evidence. +. Do not modify credentials or run package installation, network deployment, + release, container, or firewall commands as part of a source review. +. In pull requests, state what ran, what did not run, the relevant toolchain + versions, and what remains unverified. + +== Disabled operations + +There is no `just setup`, `just install`, or `just build-release` success path. +`just doctor` only reports local tool availability and Git state; it does not +repair or install anything. `just assail` requires the optional external +`panic-attack` scanner. `setup.sh` is a non-mutating information helper. +Release and Pages publishing workflows have been removed; container and cloud +deployment are not supported deliverables. Do not reintroduce these operations +until a package-specific artifact and reproducible runtime test plan have been +reviewed. + +== Handoff + +When compiler access is unavailable, leave the checkout explicitly +`not compiler-verified`. The next maintainer should run the full configured +Idris2 and Zig matrix in a suitable environment, investigate failures by +package, and update the evidence files only after reproducing results. diff --git a/QUICKSTART-USER.adoc b/QUICKSTART-USER.adoc index 76b027e1..f74401e0 100644 --- a/QUICKSTART-USER.adoc +++ b/QUICKSTART-USER.adoc @@ -1,125 +1,56 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -// Template: QUICKSTART-USER.adoc — 5-minute path to working software -// Replace proven-servers, Proven Servers — See README.adoc for details., just run, Proven Servers started successfully. with actuals -= proven-servers — Quick Start for Users += proven-servers — Reader Quick Start :toc: :toclevels: 2 -== What is proven-servers? +[IMPORTANT] +==== +There is currently no end-user server or installable product to run. This +repository contains research/prototype source, protocol models, FFI experiments, +and binding scaffolds. Do not deploy these sources as a production server. +==== -Proven Servers — See README.adoc for details. - -== Prerequisites - -Before you begin, ensure you have: - -* **just** — task runner (https://github.com/casey/just[install guide]) -* Platform-specific requirements listed below - -[cols="1,3"] -|=== -| Platform | Additional Requirements - -| Linux -| See README.adoc - -| macOS -| See README.adoc - -| Windows -| See README.adoc -|=== - -== Install - -=== Option 1: Standard Install (recommended) +== Explore the source [source,bash] ---- -# Clone and set up git clone https://github.com/hyperpolymath/proven-servers.git cd proven-servers -just setup ----- - -The setup script will: - -* Detect your platform and shell -* Install missing dependencies (with your permission) -* Configure the application -* Offer install location choices -* Run a self-diagnostic to verify everything works - -=== Option 2: Container (via Stapeln) - -[source,bash] ----- -just stapeln-run ----- - -=== Option 3: Portable (no system changes) - -[source,bash] ----- -just install --portable --prefix=./proven-servers-portable ----- - -== First Run - -[source,bash] ----- -just run ----- - -Expected output: - -[source] ----- -Proven Servers started successfully. ----- - -== Self-Diagnostic - -If something isn't working: - -[source,bash] ----- -just doctor ----- - -This checks all dependencies, permissions, paths, and connectivity. -If it finds issues, it will suggest fixes. - -To attempt automatic repair: - -[source,bash] ----- -just heal +less README.adoc +less READINESS.adoc +less PROOF-NEEDS.adoc ---- -== Get Help +Each package README describes its own scope and evidence limits. A directory +named after a protocol does not imply a complete server or verified support. -* **In-app**: `just run --help` -* **Guided tour**: `just tour` -* **Report a problem**: `just help-me` (pre-fills diagnostic context) -* **Wiki**: https://github.com/hyperpolymath/proven-servers/wiki +== Optional package checks -== Uninstall +The task entry point is https://github.com/casey/just[Just]. Idris2 and Zig are +required for compiler-backed checks; the root `mise.toml` pins Just only. The +compiler versions and full package matrix are not yet reproducibly pinned. [source,bash] ---- -just uninstall +just info +just deps +just build-idris +just build-zig +just test-zig ---- -You will be asked: +`just deps` reports tool availability; it does not install anything. Missing +compilers cause compiler-backed tasks to fail. See `QUICKSTART-DEV.adoc` before +running checks, and do not interpret static smoke tests as proofs or protocol +conformance. -1. Which uninstall tier (Bennett reversible, parameter-based, standard, or secure) -2. Whether to include or exclude your data -3. Whether to clear caches and LLM models +== Not available -== Next Steps +There is no supported `just setup`, `just run`, `just install`, container +build, deployment, or release flow. `just doctor` only reports local tool +availability and Git state; it does not repair the environment. `setup.sh` is a +non-mutating information helper. Package-specific experimental code is not an +end-user service. -* Read the link:README.adoc[README] for full feature overview -* Read the link:EXPLAINME.adoc[EXPLAINME] for architecture and design decisions -* Try `just tour` for a guided walkthrough +For vulnerability reports, see `SECURITY.adoc` and `.well-known/security.txt`. diff --git a/READINESS.adoc b/READINESS.adoc index 878c6542..bd0cf445 100644 --- a/READINESS.adoc +++ b/READINESS.adoc @@ -1,109 +1,80 @@ -== proven-servers Component Readiness Assessment - -*Standard:* -https://github.com/hyperpolymath/standards/tree/main/component-readiness-grades[Component -Readiness Grades (CRG) v1.0] *Assessed:* 2026-03-02 *Assessor:* Jonathan -D.A. Jewell - -*Current Grade:* C - -=== Grade Reference - -[width="100%",cols="8%,22%,19%,51%",options="header",] -|=== -|Grade |Name |Release Stage |Meaning -|X |Untested |— |No testing performed. Status unknown. - -|F |Harmful / Wasteful |— |Reject, deprecate, or delegate. - -|E |Minimal / Salvageable |Pre-alpha |Barely functional. Needs redesign -or major work. - -|D |Partial / Inconsistent |Alpha |Works on some things but not -systematically. - -|C |Self-Validated |Beta |Dogfooded and reliable in home context. - -|B |Broadly Validated |Release Candidate |Tested on 6+ diverse external -targets. - -|A |Field-Proven |Stable |Real-world feedback confirms value. No harm in -wild. -|=== - -=== Component Assessment - -Assessed by component group. Evidence is tool-backed (see -`+audits/proof-panic-attack-2026-06-23.md+`): `+idris2 --build+` -type-checks the proofs; `+zig build test+` exercises the engines. - -[width="100%",cols="26%,8%,8%,18%,22%,18%",options="header",] -|=== -|Component group |Count |Grade |Release Stage |Evidence Summary |Last -Assessed -|Connector ABI-FFI (`+connectors/proven-*conn+`) |6 |B |Release -Candidate |Full RSR stack: Idris2 ABI proofs + generated C headers + Zig -FFI; exercised via Rust/Gleam/Elixir bindings. |2026-06-23 - -|Protocol cores (`+protocols/proven-*+`) |88 |C |Beta |Idris2 types + -proofs compile under `+%default total+` (96/96 packages); Zig engines -pass FFI tests (98/98). In-memory skeletons, dogfooded; not externally -validated. |2026-06-23 - -|Reference servers (`+proven-timestamp+`, `+proven-quic+`, -`+proven-http3+`) |3 |C |Beta |As above, plus deeper proofs (validator -decidability, exhaustive conformance, universal safety theorems) and -reproducible RFC/test vectors. |2026-06-23 - -|Core primitives (`+core/proven-*+`) |8 |C |Beta |Idris2 cores compile; -Zig engines pass where present. |2026-06-23 - -|Language bindings (`+bindings/+`) |20 langs |D |Alpha |20 bindings over -the generated C ABI. Ada (`+pragma Import+`) and Java (JNI) are fully -FFI-wired (reference); most others (Rust/Go/Python/OCaml/Julia/…) call -`+libproven_*+` via FFI; Elixir/Gleam/ReScript are constants-only -scaffolds after unproven reimplementations were removed (ADR 0003). -Binding constants not yet cross-tested against the C ABI. |2026-06-24 -|=== - -=== Detailed Assessment - -==== Protocol cores — Grade C (Beta) - -* *Evidence:* All 96 Idris2 packages type-check (proofs verified); all -98 Zig FFI engines pass `+zig build test+`; no proof-escape hatches and -no `+@panic+`/`+unreachable+` in engine code. -* *Known limitations:* the proofs constrain an Idris _model_; the Zig -engine that runs is linked to it by hand + tests, not by a -machine-checked proof (the model–implementation gap). Engines are -in-memory state machines — no real networking, TLS/crypto, or -persistence. 16 protocols still carry an unverified (orphaned) ABI -pending migration. -* *Promotion path (→ B):* close the model–implementation gap (exhaustive -conformance / single-sourced tags), make every transition relation -`+Dec+`, and validate on 6+ external targets via the bindings. -* *Demotion risk:* Low — the build + test gates are green and -reproducible. - -==== Connector ABI-FFI — Grade B (Release Candidate) - -* *Evidence:* the 6 connectors are the only components with the -complete, documented ABI-FFI layer and multi-language binding coverage. -* *Promotion path (→ A):* real-world deployment feedback. -* *Demotion risk:* Low. - -=== Notes - -* Grades are per-component, not per-project. -* Grade A does not mean perfection — it means demonstrated value in the -field. -* Grade F includes opportunity cost — maintaining something when a -better tool exists. -* Grades can be skipped if evidence supports it (e.g., X → C if -dogfooded immediately). -* Review all grades before each release and at least once per release -cycle. -* See the -https://github.com/hyperpolymath/standards/tree/main/component-readiness-grades[full -CRG standard] for complete definitions, evidence requirements, and -transition criteria. +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += proven-servers Readiness and Evidence Status + +:revdate: 2026-09-27 +:toc: + +*Assessment date:* 2026-09-27 +*Assessor:* repository source and test-harness inspection +*Current grade:* X — not assessed for the current checkout + +== Scope of this assessment + +This is a conservative status report, not a release certification. It records +what could be established by source inspection in the current workspace. The +Idris2, Zig, OCaml, Dune, and Just toolchains were unavailable here, so no +compiler, native test suite, or package-manager check was run. The current +branch's modified Idris2 and Zig sources have therefore not been build-verified. + +Top-level package-directory counts are inventory only: 88 protocol directories, +5 core directories, 6 connector directories, and 20 binding directories. They +do not mean those packages build, pass tests, provide production services, or +have proven implementations. + +== Evidence available now + +* Source inspection confirmed several FFI operations are deliberately + fail-closed: Authserver authentication does not succeed; PQC cryptographic + entry points reject; DNSSEC key loading/signing/validation are unavailable; + and OCaml native calls raise an explicit unavailable error pending compatible + C stubs. +* The direct OCaml `external` declarations were removed because Zig's raw C + exports are not OCaml runtime primitives. `bindings/ocaml/README.adoc` + documents the disabled boundary. +* The DNS model/FFI is a bounded message-builder subset, not a general resolver. + Its parser and response limits, unsupported DNSSEC operations, and binding + guards are documented in the relevant package and binding source. +* The static source-check scripts passed after their false positives and + misleading labels were corrected. They are not runtime tests or security + certification. +* The binding registry check passed for the 20 on-disk directories and now + records OCaml as unavailable. This does not establish native linking for the + other language wrappers. +* The only previously recorded `git diff --check` covered the PQC subset. A + whole-branch whitespace check is still required before merge. + +== Historical evidence (not current verification) + +`audits/proof-panic-attack-2026-06-23.adoc` and +`audits/audit-ffi-2026-05-26.adoc` are dated reports. They record earlier +compiler/test runs and source-audit claims for earlier revisions. They are +useful historical records, but the current checkout contains later changes and +has not been rebuilt here; do not reuse their counts or results as current +release evidence. + +Likewise, prior versions of this file and other project documentation assigned +component grades and claimed blanket build/test success. Those statements are +withdrawn as current status until reproduced against a named commit, toolchain, +and test command. + +== Release-readiness requirements + +No component should receive a readiness grade or be called production-ready +until its evidence record identifies the exact package and revision and shows: + +. Idris2 type-checking for the relevant `.ipkg` files, if Idris code is in scope. +. Zig build and runtime tests for the corresponding implementation, if Zig FFI + is in scope. +. Generated-header/ABI consistency checks and boundary tests for the exact + interface used. +. Executed language-binding build/link tests for each binding claimed as + supported; source declarations alone are insufficient. +. Protocol-specific test vectors, negative cases, and external interoperability + tests where applicable. +. Security review for cryptography, authentication, authorization, input + validation, concurrency, and memory handling. +. Clear separation of model-level proofs from implementation-level evidence. + +A green source grep, a passing inventory script, or a clean Idris model build is +not enough to promote a native server implementation to a release grade. diff --git a/README.adoc b/README.adoc index 1df94ce5..f360b077 100644 --- a/README.adoc +++ b/README.adoc @@ -1,142 +1,144 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -= proven-servers — Formally Verified Server Cores -image:https://img.shields.io/badge/OpenSSF-Best_Practices-green?logo=openssourcesecurity[OpenSSF Best Practices,link="https://www.bestpractices.dev/en/projects/new?repo_url=https://github.com/hyperpolymath/proven-servers"] += proven-servers — Protocol Models and FFI Prototypes :author: Jonathan D.A. Jewell :email: j.d.a.jewell@open.ac.uk -:revdate: 2026-03-01 -:license: MPL-2.0 - -== What This Is - -A catalog of **109 formally verified server components** written in Idris 2 with -dependent types: **95 protocol skeletons**, **8 core primitives**, and **6 connector -interfaces**. - -Each component defines the *correct types* for its domain: message types, valid states, -state transitions, error codes. Machine-checked. Proven safe at compile time. - -The 6 connector interfaces have a complete **ABI-FFI layer**: Idris2 ABI definitions -with dependent-type proofs, generated C headers, and Zig FFI implementations with -integration tests. Any language that can call C can use them. - -**This is the core. Nothing else.** No frameworks. No opinions. No "batteries included." -You get the verified bones. You build the rest. - -== Who This Is For - -**If you want to learn Idris 2:** Read the source. It's clean, documented, and teaches you -both the protocol and the language. - -**If you never want to touch Idris 2:** Use the FFI bindings from Rust, Go, Python, Elixir, or -any of the 20 supported languages. Your server core is still formally verified. You don't -care how. It just is. +:revdate: 2026-09-27 +:license: CC-BY-SA-4.0 +:toc: +:toclevels: 2 + +[IMPORTANT] +==== +*This repository is not a collection of production-ready or formally verified +server implementations.* It contains protocol models, Idris2 source, Zig FFI +prototypes, native-binding source, and scaffolding with varying levels of +implementation and verification. Directory counts are inventory only. Do not +infer security, interoperability, or release readiness from the name +`proven-servers`, a type definition, or a source-level smoke check. +==== + +== What is in this repository + +The repository is a monorepo of protocol and connector models, per-package +FFI experiments, language-binding sources, documentation, and audit artefacts. +As of 2026-09-27, its top-level directory inventory is: + +[cols="1,1,3"] +|=== +|Area |Directories |What the count means -Either way, your server's protocol handling is *proven correct*. +|`protocols/` |88 |Protocol-named package directories; not 88 working servers. +|`core/` |5 |`audit`, `cli`, `compose`, `frame`, and `fsm` package directories. +|`connectors/` |6 |`cacheconn`, `dbconn`, `nesy-solver-api`, `queueconn`, `resolverconn`, and `storageconn` directories. +|`bindings/` |20 |Language-named source directories; not 20 supported or tested bindings. +|=== -== Structure +The protocol directory names are: + +`agentic` `airgap` `amqp` `apiserver` `appserver` `authserver` `backup` `bfd` +`ca` `cache` `caldav` `carddav` `chat` `coap` `configmgmt` `ctlog` `dds` +`deception` `diode` `doh` `doq` `dot` `epistemic` `federation` `fileserver` +`ftp` `gameserver` `git` `graphdb` `graphql` `grpc` `hardened` `honeypot` +`http3` `ids` `imap` `irc` `kerberos` `kms` `ldap` `ldp` `loadbalancer` +`logcollector` `lpd` `mcp` `mdns` `media` `metrics` `modbus` `monitor` `mqtt` +`nesy` `netconf` `neurosym` `objectstore` `ocsp` `odns` `opcua` `ospf` `pop3` +`pqc` `proxy` `ptp` `quic` `radius` `rtsp` `sandbox` `sdn` `semweb` `siem` +`smb` `smtp` `snmp` `socks` `sparql` `stun` `tacacs` `telnet` `timestamp` +`triplestore` `virt` `voip` `vpn` `wasm` `webdav` `ws` `xmpp` `zerotrust` + +== Verification boundary + +An Idris2 proof, when the relevant `.ipkg` builds successfully with the declared +compiler, is a proof about the proposition encoded in that Idris2 model. It does +*not* by itself prove that a separate Zig implementation, generated header, +network service, or language binding conforms to the model. That relationship +requires independently reproducible conformance tests and a verified bridge; +there is no repository-wide machine-checked proof of that relationship here. + +Build and test evidence is package- and revision-specific. The recorded audits +under `audits/` are dated snapshots, not evidence for later source changes. The +shell checks under `tests/` are source-pattern heuristics or partial samples; +they are not formal proofs, exhaustive protocol conformance suites, or security +certifications. Several operations intentionally fail closed rather than claim +functionality: for example, the OCaml native calls raise an unavailable error, +Authserver authentication is rejected, PQC operations reject, and DNSSEC +signing/validation are unavailable. + +Before treating any component as usable, inspect its own README and ABI, build +it with the declared toolchain, run its implementation tests, verify generated +artifacts, and assess its protocol/security requirements. Do not use this +repository as a production server or cryptographic implementation on the +strength of these sources alone. + +== Repository layout [source] ---- proven-servers/ -├── core/ # 8 primitives (socket, frame, fsm, wire, compose, -│ # tls, config, audit) -├── protocols/ # 95 protocol skeletons -│ ├── proven-dns/ # Each with its own .ipkg and src/ -│ ├── proven-smtp/ -│ ├── proven-mqtt/ -│ └── ... -├── connectors/ # 6 connector interfaces (with ABI-FFI) -│ ├── proven-dbconn/ # Database connections -│ ├── proven-authconn/ # Authentication lifecycle -│ ├── proven-cacheconn/ # Cache connections -│ ├── proven-queueconn/ # Message queue pub/sub -│ ├── proven-resolverconn/ # DNS resolution -│ └── proven-storageconn/ # Object storage -├── abi/ # Idris2 ABI definitions (src/abi/*.idr) -├── ffi/ # Zig FFI layer (ffi/zig/) -└── bindings/ # 20 language bindings over the C ABI (Ada, C++, C#, - # Dart, Elixir, Gleam, Go, Haskell, Java, JavaScript, - # Julia, Kotlin, Lua, OCaml, PHP, Python, ReScript, - # Ruby, Rust, Swift) +├── protocols/ # 88 protocol-named source packages +├── core/ # Shared model/FFI packages +├── connectors/ # Connector and integration packages +├── bindings/ # 20 language-named source directories +├── not-proven/ # Explicitly non-proven examples/prototypes +├── tests/ # Sample runtime and source-pattern checks +├── audits/ # Dated historical audit records +├── generated/ # ABI/header artefacts; generation status varies +├── ffi/ # Root-level Zig FFI sources +├── src/abi/ # Root-level Idris2 ABI sources +└── .machine_readable/ # State, policies, registry, and agent guidance ---- -== Protocol Catalog (95 skeletons) - -=== Network Protocols -`dns` `smtp` `httpd` `ftp` `imap` `pop3` `irc` `xmpp` `mqtt` `ldap` -`radius` `snmp` `nfs` `smb` `syslog` `tftp` `dhcp` `mdns` `coap` `amqp` -`grpc` `graphql` `modbus` `netconf` `opcua` `telnet` `rtsp` `voip` `stun` -`bgp` `ospf` `bfd` `sdn` `ptp` `ntp` `nts` `ws` - -=== Security -`firewall` `vpn` `ids` `siem` `honeypot` `deception` `ca` `ocsp` `kms` -`pqc` `zerotrust` `ctlog` `airgap` `diode` `sandbox` `hardened` -`authserver` `kerberos` `socks` `tacacs` `ssh-bastion` `epistemic` - -=== DNS Variants -`doh` `dot` `doq` `odns` - -=== Application / Data -`proxy` `loadbalancer` `apiserver` `appserver` `fileserver` `dbserver` -`objectstore` `cache` `webdav` `graphdb` `sparql` `triplestore` `semweb` `ldp` - -=== Operations -`monitor` `metrics` `logcollector` `configmgmt` `backup` `container` `virt` `git` - -=== Media / Communication -`media` `chat` `gameserver` - -=== AI / Neurosymbolic -`neurosym` `agentic` `mcp` `federation` - -=== Other -`cli` `wasm` `lpd` - -== Core Primitives (8) - -`socket` `frame` `fsm` `wire` `compose` `tls` `config` `audit` - -== Connector Interfaces (6, with ABI-FFI) - -[cols="1,2,1"] -|=== -| Connector | Purpose | States - -| `dbconn` | Relational databases (query, transaction, prepare) | 5 -| `authconn` | Authentication lifecycle (MFA, tokens, lockout) | 6 -| `cacheconn` | Cache connections (TTL, eviction, degradation) | 4 -| `queueconn` | Message queues (subscribe, publish, ack/reject) | 5 -| `resolverconn` | DNS resolution (13 RR types, DNSSEC, caching) | 4 -| `storageconn` | Object storage (upload, download, integrity) | 5 -|=== - -Each connector has a complete ABI-FFI layer: Idris2 proofs, C headers, Zig FFI, -and integration tests. See `connectors/README.adoc` for details. - -== Design Principles - -1. **Secure by default.** If there is a secure and insecure version of something, - only the secure version exists here. -2. **Minimal.** Only the types needed to define protocol correctness. No helpers, - no utilities, no extras. -3. **Composable.** Protocols are atoms. Oblivious DNS is `odns` relay + `dns`. - An OIDC proxy is `proxy` + `authserver`. Compose them; don't create new skeletons. -4. **Proven.** Every type is total. No `believe_me`. No `assert_total`. No escape hatches. -5. **Permanent.** Protocol types don't change. DNS message types from 1987 are still - the same types today. This code is meant to last. - -== ABI / FFI Standard - -* **ABI** (Idris 2): Interface definitions with dependent type proofs → `abi/` -* **FFI** (Zig): C-compatible implementation → `ffi/` -* **Bindings**: Thin wrappers for 20 languages → `bindings/` - -See `ABI-FFI-README.md` for the full standard. +Most protocol packages keep source, `.ipkg` files, Zig `build.zig` files, +tests, and generated ABI artefacts under that package. Presence and coverage +vary; there is no requirement that every directory provide every layer. + +== Bindings status + +`bindings/` contains 20 language-named directories. This is an inventory, not a +supported-language promise. Many directories contain only declarations or +source wrappers, and their native linking and tests have not been re-established +as a single cross-language matrix. The OCaml tree is explicitly unavailable +until OCaml-compatible C stubs are implemented; Java/Kotlin native method +declarations likewise do not establish that a JNI bridge exists. See +`.machine_readable/BINDINGS.a2ml` and `bindings/ocaml/README.adoc` for the +current qualifications. + +== Local checks + +The top-level `Justfile` is the task entry point. `just build` and `just test` +are intended to run real Idris2/Zig checks and fail if required tools are +missing; they are not success-printing placeholders. `bash tests/e2e.sh` runs a +selected Idris2/Zig package test sweep; it requires both compilers and does not +establish cross-language conformance or a full E2E server. +`bash tests/source_smoke_test.sh`, `bash tests/aspect/security_test.sh`, and +`bash tests/binding_inventory.sh` are explicitly static/source-inventory checks; +they do not execute language bindings or prove cross-language conformance. + +The 2026-06-23 audit reports are historical. In the current audit environment +Idris2, Zig, OCaml, Dune, and Just were unavailable, so no build or test suite +could be run here. See `READINESS.adoc` and `PROOF-NEEDS.adoc` for the present +status and concrete evidence needed next. + +== Release and deployment status + +No validated executable server or release artifact is produced by this +repository. Root container/cloud deployment, package release, and Pages +publishing automation are disabled or removed; prototype source under +`connectors/` is not a deployment target. Do not infer operational readiness +from a `Containerfile`, endpoint name, deployment script, or workflow without +reproducible package and runtime evidence. + +== Related documentation + +* link:READINESS.adoc[Readiness and current evidence] +* link:PROOF-NEEDS.adoc[Proof and verification gaps] +* link:ABI-FFI-README.adoc[ABI/FFI status and build notes] +* link:QUICKSTART-DEV.adoc[Developer quick start] +* link:SECURITY.adoc[Security policy] == License -This project is licensed under the Mozilla Public License, v. 2.0. See the `LICENSE` file for details. - -SPDX-License-Identifier: CC-BY-SA-4.0 \ No newline at end of file +The software is licensed under the Mozilla Public License 2.0; see `LICENSE`. +This README is licensed under CC-BY-SA-4.0 as indicated by its SPDX header. diff --git a/ROADMAP.adoc b/ROADMAP.adoc index 00cb6440..4f056981 100644 --- a/ROADMAP.adoc +++ b/ROADMAP.adoc @@ -1,142 +1,78 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= proven-servers Roadmap -:author: Jonathan D.A. Jewell -:revdate: 2026-04-11 - -== Current State - -A catalog of formally verified server components in Idris2 with dependent types. -94 modules proven (in core/, protocols/, connectors/), 19 modules in not-proven/. -Total: 113 modules. Completion: 83%. - -Proven breakdown: - -* **84 protocol skeletons** in protocols/ (proven, type-checked) -* **5 core primitives** in core/ (proven: audit, cli, compose, frame, fsm) -* **5 connector interfaces** in connectors/ (proven: cacheconn, dbconn, queueconn, resolverconn, storageconn) - -ABI-FFI status: - -* 6/6 connectors: complete ABI-FFI pairs (Idris2 proofs, C headers, Zig FFI, integration tests) -* 8/8 core primitives: complete ABI-FFI pairs -* 26 protocols: full ABI-FFI pairs -* 58 protocols: type definitions only (skeleton, no ABI-FFI yet) -* 20 language bindings (thin wrappers over the C ABI): Ada, C++, C#, Dart, Elixir, Gleam, Go, Haskell, Java, JavaScript, Julia, Kotlin, Lua, OCaml, PHP, Python, ReScript, Ruby, Rust, Swift - -19 not-proven modules: authconn, bgp, config, container, dbserver, deception, dhcp, -firewall, httpd, nfs, ntp, nts, socket, ssh-bastion, syslog, tftp, tls, -typed-frame-router, wire. - -== v0.9.0 -- Core ABI-FFI (Complete) - -* [x] 6/6 connector ABI-FFI pairs (authconn, dbconn, cacheconn, queueconn, resolverconn, storageconn) -* [x] 8/8 core primitive ABI-FFI pairs (socket, frame, fsm, wire, compose, tls, config, audit) -* [x] Generated C headers for all ABI-FFI pairs -* [x] Zig FFI implementations with integration tests -* [x] 20-language bindings (Ada, C++, C#, Dart, Elixir, Gleam, Go, Haskell, Java, JavaScript, Julia, Kotlin, Lua, OCaml, PHP, Python, ReScript, Ruby, Rust, Swift) - -== v0.10.0 -- Protocol ABI-FFI Expansion (In Progress) - -* [x] 26/84 protocol skeletons with full ABI-FFI pairs -* [ ] Remaining 58 protocols: add Layout.idr, Transitions.idr, Foreign.idr, C headers, Zig FFI -* [ ] CI/CD integration for ABI-FFI validation - -== v0.11.0 -- Prove Remaining 19 Modules - -* [ ] proven-authconn (promote from not-proven/) -* [ ] proven-bgp -* [ ] proven-config -* [ ] proven-container -* [ ] proven-dbserver -* [ ] proven-deception -* [ ] proven-dhcp -* [ ] proven-firewall -* [ ] proven-httpd -* [ ] proven-nfs -* [ ] proven-ntp -* [ ] proven-nts -* [ ] proven-socket -* [ ] proven-ssh-bastion -* [ ] proven-syslog -* [ ] proven-tftp -* [ ] proven-tls -* [ ] proven-typed-frame-router -* [ ] proven-wire - -== v1.0.0 -- Stable Release - -* [ ] All 113 modules proven (0 in not-proven/) -* [ ] All protocols with full ABI-FFI pairs -* [ ] Comprehensive integration test suite -* [ ] No believe_me, assert_total, or unsafe patterns anywhere -* [ ] OpenSSF badge - -== v1.1.0 -- HTTP Capability Gateway + Consent Layer - -Integrate the estate's HTTP-layer tooling as first-class proven-servers consumers. -Both repos exist and have ABI-FFI skeletons; what is missing is wiring into the -proven-httpd + proven-typed-frame-router proof obligations and the nesy-solver-api -deployment stack. - -* [ ] **proven-httpd** — promote from not-proven/; Idris2 capability-typed HTTP handler proofs -* [ ] **proven-typed-frame-router** — promote from not-proven/; typed-frame dispatch proof obligations -* [ ] **hybrid-automation-router integration** — wire har-core event types through proven-queueconn - dispatch; verified routing decisions backed by proven-fsm states -* [ ] **conative-gating (consent-aware HTTP)** — wrap proven-httpd with conative-gating's - consent-checking middleware; dependent-type proof that a request cannot be forwarded - without a resolved consent record -* [ ] **Invariant path tool** — expose proven-typed-frame-router as an invariant-path - checker for the nesy-solver-api: each `/prove` request traverses a verified route - that cannot skip consent, audit, or trust-level gates -* [ ] ABI-FFI pairs for proven-httpd + proven-typed-frame-router -* [ ] Integration tests: consent-denied → 403, missing trust-level → 403, valid path → 200 - -== v1.2.0 -- Hypatia + CI Intelligence - -* [ ] **Hypatia CI scan integration** — run `hypatia scan` against all proven-servers - modules in CI; block merge if trust-pipeline invariants regress -* [ ] **Hypatia → proven-servers feedback loop** — Hypatia findings routed back as - Idris2 proof obligations (currently listed as "potential-consumer" in ECOSYSTEM.a2ml) -* [ ] **Automated ABI compliance check** — Hypatia rule verifying Idris2 ABI → C header - → Zig FFI consistency for all 84 protocol skeletons -* [ ] Wire Hypatia scan results into STATE.a2ml via the 6A2 innervation pipeline - -== v1.3.0 -- VeriSimDB + Lithoglyph Aqueduct - -The aqueduct pipeline spec already exists at -`verisimdb/.machine_readable/integrations/aqueduct.a2ml`. -What is missing is the actual implementation of each stage. - -* [ ] **Stage 1 — sanitise**: strip PII/secrets from raw proof attempt records before - they leave the nesy-solver-api boundary -* [ ] **Stage 2 — structure**: convert `VerisimAttempt` JSON into `ConvStruct` A2ML -* [ ] **Stage 3 — lithoglyph_insert**: inscribe structured attempt as a permanent narrative - artefact in Lithoglyph; `LithoInscription` type (permanent, side-effect) -* [ ] **Stage 4 — verisim_ingest**: derive counterfactual record in VeriSimDB from the - inscription (closes the loop: every proved obligation has both a graph inscription - and a counterfactual drift record) -* [ ] Linear-type enforcement: no attempt record can skip inscription; no inscription - can be consumed twice (Ephapax-style affine token) -* [ ] Wire aqueduct as a post-handler in nesy-solver-api `/prove` response path - -== v1.4.0 -- ReScript TEA + Cadre Router - -* [ ] **cadre-tea-router** (`developer-ecosystem/rescript-ecosystem/cadre-tea-router`) - — wire as the routing layer for the nesy-solver playground UI; replaces ad-hoc - ReScript component wiring with a typed TEA message bus -* [ ] **ReScript TEA pattern** — refactor echidna's 33 ReScript UI components to route - all messages through the cadre-tea-router; no direct DOM side-effects outside TEA - update loop -* [ ] Type-safe message dispatch: each user action (submit proof, select prover, view - history) modelled as a variant in the TEA `Msg` type -* [ ] Cadre router integration test: invalid message shape → compile error, not runtime crash - -== Future - -* Additional protocol skeletons as new protocols emerge -* Cross-protocol composition examples (e.g. OIDC proxy = proxy + authserver) -* Performance benchmarks for FFI call overhead -* Stapeln container deployment for verified server bundles -* Groove protocol integration: all inter-service calls (nesy-solver-api ↔ echidna ↔ - verisim-api) routed through Groove rather than direct HTTP +// Copyright (c) 2026 Jonathan D.A. Jewell += proven-servers — Verification Roadmap +:revdate: 2026-09-27 +:toc: +:sectnums: + +[IMPORTANT] +==== +This is an evidence-first worklist, not a release schedule or feature-completion +claim. The repository is a research/prototype source monorepo. No component +counts or percentages below imply readiness. +==== + +== Current position + +The current checkout has not been compiler-verified in the assessment +workspace: Just, Idris2, and Zig were unavailable. Package-directory counts in +`README.adoc` and `TOPOLOGY.adoc` are inventory only. Historical version labels, +completion percentages, ABI-pair counts, and deployment milestones from older +roadmaps are withdrawn as current status until reproduced. + +== Must — establish reproducible evidence + +. Run `just build-idris`, `just build-zig`, and `just test-zig` on a named + revision with recorded package-specific toolchain versions. Triage failures + without suppressing them; define a reproducible supported toolchain matrix. +. Run the source-only checks (`just test-static`) and report them separately + from compiler-backed tests and formal proof. +. Exercise package-specific runtime/error-path tests for behavior that is + currently represented only by source declarations or heuristics. +. Verify generated/hand-maintained headers, symbols, layouts, ownership rules, + and calling conventions against their source packages; add a checked + generation or conformance path before claiming ABI-wide parity. +. Compile, link, and execute each language binding before describing it as + wired or supported; preserve explicit unavailable states for missing JNI, + NIF, OCaml stubs, or other bridges. +. Keep cryptographic, authentication, and other security-sensitive paths + fail-closed until backed by reviewed implementations and vectors. + +== Should — improve test quality + +* Add independent protocol fixtures and negative/malformed-input cases to + packages with maintained executable targets. +* Add wire-level interoperability tests only for protocols whose concrete + implementation and test peer exist. +* Add concurrency, resource-limit, and fuzz tests where a runnable target and + safe harness are available. No fuzz harness is currently configured. +* Make test output distinguish missing package targets, skipped tools, source + heuristics, and executed tests. +* Resolve mutable CI action references and review workflow permissions; an + action-lock inventory is not a runtime SHA-pin enforcement mechanism. + +== Could — only after a runnable baseline + +* Add a reproducible benchmark target with documented inputs and measurements; + the presence of source under `bindings/rust/benches/` is not benchmark + evidence. +* Develop a package-specific service only if a concrete network runtime, + authentication model, tests, and maintainers are available. +* Reconsider packaging or deployment only after a reproducible artifact build, + runtime health/error tests, security review, and explicit maintainer approval. + +== Explicitly not scheduled + +No release version, production rollout, container publication, Pages site, +Hypatia-to-proof feedback loop, NESY/Fly.io deployment, or external +Echidna/VerisimDB/Lithoglyph/Groove integration is committed by this roadmap. +Those names appear in prototype or historical source; they are not current +operational dependencies or verified project capabilities. + +== Status updates + +Update this roadmap only when work is actually reprioritized or evidence is +reproduced. Record exact revision, toolchain versions, commands, and outcomes +in `READINESS.adoc`, `PROOF-NEEDS.adoc`, and +`.machine_readable/6a2/STATE.a2ml`. Never replace unknown status with a guessed +percentage. diff --git a/RSR_OUTLINE.adoc b/RSR_OUTLINE.adoc index 24fdffbf..4918e68f 100644 --- a/RSR_OUTLINE.adoc +++ b/RSR_OUTLINE.adoc @@ -1,292 +1,75 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -= RSR Template Repository - -image:[Palimpsest-MPL-1.0,link="https://github.com/hyperpolymath/palimpsest-license"] image:[Palimpsest,link="https://github.com/hyperpolymath/palimpsest-license"] += RSR Alignment Notes — proven-servers :toc: :sectnums: - -// Badges -image:https://img.shields.io/badge/RSR-Infrastructure-cd7f32[RSR Infrastructure] -image:https://img.shields.io/badge/Phase-Maintenance-brightgreen[Phase] -image:https://img.shields.io/badge/Guix-Primary-purple?logo=gnu[Guix] - -== Overview - -**The canonical template for RSR (Rhodium Standard Repository) projects.** - -This repository provides the standardized structure, configuration, and tooling for all RSR-compliant repos. Use it to: - -* Bootstrap new projects with RSR compliance -* Reference the standard directory structure -* Copy configuration templates (Justfile, STATE.a2ml, etc.) - -== Quick Start - -[source,bash] ----- -# Clone the template -git clone https://github.com/hyperpolymath/RSR-template-repo my-project -cd my-project - -# Remove template git history -rm -rf .git -git init - -# Interactive bootstrap — replaces all placeholders -just init - -# Enter development environment -guix shell -D -f build/guix.scm - -# Validate compliance -just validate-rsr ----- - -== What's Included - -[cols="1,3"] -|=== -|File/Directory |Purpose - -|`.editorconfig` -|Editor configuration (indent, charset) - -|`.gitignore` -|Standard ignore patterns - -|`.gitattributes` -|Line endings, diff drivers, binary detection - -|`.guix-channel` -|Guix channel definition - -|`.well-known/` -|RFC-compliant metadata (security.txt, ai.txt, humans.txt) - -|`.machine_readable/` -|All machine-readable content: state files (6 a2ml), `bot_directives/`, `contractiles/` - -|`docs/` -|Documentation directory - -|`guix.scm` -|Guix package definition - -|`Justfile` -|Task runner with 40+ recipes - -|`Containerfile` -|Container build (Wolfi base, Podman) - -|`LICENSE` -|MPL-2.0 (Palimpsest MPL) - -|`EXHIBIT-A-ETHICAL-USE.txt` -|Ethical use guidelines (LICENSE Exhibit A) - -|`EXHIBIT-B-QUANTUM-SAFE.txt` -|Quantum-safe provenance spec (LICENSE Exhibit B) - -|`README.adoc` -|Project overview - -|`TOPOLOGY.md` -|Architecture diagram and completion dashboard - -|`PLACEHOLDERS.md` -|Template variable reference and replacement guide - -|`0-AI-MANIFEST.a2ml` -|Universal AI agent entry point - -|`AI.a2ml` -|Claude-specific instructions - -|`src/abi/` -|Idris2 ABI definitions (Types, Layout, Foreign) - -|`ffi/zig/` -|Zig FFI implementation - -|`generated/abi/` -|Auto-generated C headers from Idris2 ABI -|=== - -== Justfile Features - -The template Justfile provides: - -* **Combinatoric matrix recipes** for build, test, container, CI -* **Cookbook generation**: `just cookbook` -> `docs/just-cookbook.adoc` -* **Man page generation**: `just man` -> `docs/man/project.1` -* **RSR validation**: `just validate-rsr` -* **STATE.a2ml management**: `just state-touch`, `just state-phase` -* **Container support**: `just container-build`, `just container-push` -* **CI matrix**: `just ci-matrix [stage] [depth]` - -=== Key Recipes - -[source,bash] ----- -just # Show all recipes -just help # Detailed help -just info # Project info -just combinations # Show matrix options - -just build # Build (debug) -just test # Run tests -just quality # Format + lint + test -just ci # Full CI pipeline - -just validate # RSR + STATE validation -just docs # Generate all docs -just cookbook # Generate Justfile docs - -just guix-shell # Guix dev environment -just container-build # Build container ----- - -== Directory Structure - -[source] ----- -project/ -├── .editorconfig # Editor settings -├── .gitignore # Git ignore -├── .gitattributes # Line endings, diff drivers -├── .guix-channel # Guix channel -├── .well-known/ # RFC metadata -│ ├── ai.txt -│ ├── humans.txt -│ └── security.txt -├── .machine_readable/ # ALL machine-readable content -│ ├── STATE.a2ml # Project state, progress, blockers -│ ├── META.a2ml # Architecture decisions, governance -│ ├── ECOSYSTEM.a2ml # Ecosystem position, relationships -│ ├── AGENTIC.a2ml # AI agent interaction patterns -│ ├── NEUROSYM.a2ml # Neurosymbolic integration config -│ ├── PLAYBOOK.a2ml # Operational runbook -│ ├── bot_directives/ # Per-bot rules and constraints -│ └── contractiles/ # Policy enforcement contracts -│ ├── k9/ # Security levels (Kennel/Yard/Hunt) -│ ├── dust/Dustfile # Recovery and rollback -│ ├── lust/Intentfile # Future intent declarations -│ ├── must/Mustfile # Invariant checks -│ └── trust/Trustfile.hs # Cryptographic verification -├── docs/ # Documentation -│ ├── CITATIONS.adoc -│ ├── TOPOLOGY-GUIDE.adoc -│ ├── generated/ -│ └── man/ -├── src/abi/ # Idris2 ABI definitions -│ ├── Types.idr -│ ├── Layout.idr -│ └── Foreign.idr -├── ffi/zig/ # Zig FFI implementation -│ ├── build.zig -│ ├── src/main.zig -│ └── test/integration_test.zig -├── generated/abi/ # Auto-generated C headers -├── examples/ # Example code -├── guix.scm # Guix package -├── Justfile # Task runner -├── Containerfile # Container build -├── LICENSE # MPL-2.0 -├── EXHIBIT-A-ETHICAL-USE.txt # Ethical use guidelines -├── EXHIBIT-B-QUANTUM-SAFE.txt # Quantum-safe provenance -├── README.adoc # Overview -├── TOPOLOGY.md # Architecture + completion -├── PLACEHOLDERS.md # Template variable guide -├── 0-AI-MANIFEST.a2ml # Universal AI entry point -└── AI.a2ml # Claude-specific instructions ----- - -== RSR Compliance - -=== Language Tiers - -* **Tier 1** (Gold): Rust, Elixir, Zig, Ada, Haskell, ReScript, Gleam -* **Tier 2** (Silver): Nickel, Guile Scheme, Nix, Idris2, OCaml -* **Infrastructure**: Guix channels, derivations, Julia batch scripts - -=== Required Files - -* `.editorconfig` -* `.gitignore` -* `Justfile` -* `README.adoc` -* `LICENSE` (MPL-2.0) -* `.machine_readable/STATE.a2ml` -* `.well-known/security.txt` -* `.well-known/ai.txt` -* `.well-known/humans.txt` -* `guix.scm` OR `flake.nix` - -=== Prohibited - -* Python outside `salt/` directory -* TypeScript/JavaScript (use ReScript) -* CUE (use Guile/Nickel) -* `Dockerfile` (use `Containerfile`) -* npm, Bun, pnpm, yarn (use Deno) -* Go (use Rust) - -== STATE.a2ml - -The STATE.a2ml file tracks project state: - -[source] ----- -# STATE — Project State Checkpoint -# Format: a2ml (AI-readable markup) - -project: proven-servers -version: 0.1.0 -last-updated: 2026-02-14 -status: active - -phase: implementation -maturity: beta - -ecosystem: - part-of: RSR Framework - depends-on: [] - -milestones: - - name: Initial setup - completion: 100 - - name: Core implementation - completion: 0 ----- - -== Badge Schema - -Generate badges from STATE.a2ml: - -[source,bash] ----- -just badges standard ----- - -See `docs/BADGE_SCHEMA.adoc` for the full badge taxonomy. - -== Ecosystem Integration - -This template is part of: - -* **STATE.a2ml Ecosystem**: Conversation checkpoints -* **RSR Framework**: Repository standards -* **Consent-Aware-HTTP**: .well-known compliance -* **Hypatia**: Neurosymbolic security scanning -* **gitbot-fleet**: Bot orchestration - -== License - -SPDX-License-Identifier: CC-BY-SA-4.0 - -== Links - -* https://github.com/hyperpolymath/elegant-STATE[elegant-STATE] - STATE tooling -* https://github.com/hyperpolymath/conative-gating[conative-gating] - Policy enforcement -* https://rhodium.sh[Rhodium Standard] - RSR documentation +:revdate: 2026-09-27 + +== Repository role + +`proven-servers` is a project-specific research/prototype source monorepo. It is +not `rsr-template-repo`, a repository bootstrapper, or a distribution of +production-ready servers. Template structure and standards are inputs to +project-specific decisions; their maximal capability sets and example hashes +must not be copied as this repository's own evidence. + +== Capability profile + +The local profile is `.machine_readable/rsr-profile.a2ml`. It declares only the +source languages and non-trivial shell automation present here: `idris2`, +`zig`, and `bash`. + +The profile deliberately does not claim `abi`, `ffi`, `formal-proofs`, +`api-service`, `container`, `docs-site`, `published-package`, +`reproducible-build`, or `benchmarks`. In particular: + +* per-package FFI sources do not match the standards draft's gated + `src/interface/ffi/` module path, and repository-wide ABI conformance is not + established; +* Idris2 proof declarations alone do not satisfy the draft's + `verification/proofs/` gate or establish compiler-checked proof results; +* experimental HTTP-server source does not establish a deployable service; +* deployment/release/Pages scaffolding without a validated artifact is not a + shipping capability. + +These decisions follow the standards repository's capability taxonomy and +path gates rather than assuming the template's maximal profile applies here. +The external applicability policy is marked DRAFT; this file is a conservative +project inventory, not a conformance certificate. + +== Repository-specific alignment + +* `Justfile` is the actual task entry point. Build and test tasks fail when + required tools are missing; static smoke checks are explicitly narrower. +* `contractile.just` contains only bounded, non-destructive wrappers. No + automatic rollback or template bootstrap task is provided. +* The machine-readable contractile task snapshot is checked against the root + `Justfile` by `just validate-rsr`. +* Security and contribution guidance use actual repository contacts, paths, + and test-evidence limits; they do not assert an unpublished OpenPGP key or + unconfigured release process. +* Container/cloud deployment, package release, and Pages publishing remain + disabled until package-specific artifacts and runtime behavior are + reproducibly verified. + +== Validation boundary + +`just validate-rsr` checks a selected set of metadata paths and the synchronized +Justfile snapshot. It is not a full implementation of the standards reference +checker and does not certify this repository against RSR. The current checkout +has not had its Idris2 or Zig package matrix executed in the assessment +workspace because those compilers and Just are unavailable. + +Before making stronger claims, run the current standards repository's +`check-rsr-profile.sh` with its matching `template-capability-gates.toml`, then +resolve every applicable violation based on actual project scope. Independently +run the full package checks and document their exact tool versions, commit, +commands, and results. + +== Reference material + +* link:https://github.com/hyperpolymath/rsr-template-repo[rsr-template-repo] +* link:https://github.com/hyperpolymath/standards[standards] +* `0-AI-MANIFEST.a2ml` +* `.machine_readable/6a2/STATE.a2ml` +* `.machine_readable/contractiles/README.adoc` diff --git a/SECURITY.adoc b/SECURITY.adoc index 010eceae..e933266c 100644 --- a/SECURITY.adoc +++ b/SECURITY.adoc @@ -33,39 +33,20 @@ a Vulnerability] . Complete the form with as much detail as possible . Submit — we’ll receive a private notification -This method ensures: +GitHub Security Advisories provide a private reporting workflow. This is not +an end-to-end-encryption guarantee; follow GitHub's current security-advisory +privacy and access controls. -* End-to-end encryption of your report -* Private discussion space for collaboration -* Coordinated disclosure tooling -* Automatic credit when the advisory is published +==== Email Contact -==== Alternative: Encrypted Email - -If you cannot use GitHub Security Advisories, you may email us directly: - -[width="100%",cols="50%,50%",] -|=== -|*Email* |j.d.a.jewell@open.ac.uk -|*PGP Key* |https://github.com/hyperpolymath.gpg[Download Public Key] -|*Fingerprint* |`+TODO+` -|=== - -[source,bash] ----- -# Import our PGP key -curl -sSL https://github.com/hyperpolymath.gpg | gpg --import - -# Verify fingerprint -gpg --fingerprint j.d.a.jewell@open.ac.uk - -# Encrypt your report -gpg --armor --encrypt --recipient j.d.a.jewell@open.ac.uk report.txt ----- +The published contact is `j.d.a.jewell@open.ac.uk`. No verified OpenPGP public +key is currently published for this project. Do not send sensitive exploit +material by ordinary email; use the private GitHub Security Advisory workflow +or first request an appropriate protected channel. ____ -*⚠️ Important:* Do not report security vulnerabilities through public -GitHub issues, pull requests, discussions, or social media. +*Important:* Do not report security vulnerabilities through public GitHub +issues, pull requests, discussions, or social media. ____ ''''' @@ -219,15 +200,17 @@ from your initial report. === Scope -==== In Scope ✅ +==== In Scope -The following are within scope for security research: +The following are within scope for reports about this source repository: -* This repository (`+hyperpolymath/proven-servers+`) and all its code -* Official releases and packages published from this repository -* Documentation that could lead to security issues -* Build and deployment configurations in this repository -* Dependencies (report here, we’ll coordinate with upstream) +* Source, tests, build scripts, and documentation in `hyperpolymath/proven-servers` +* Published package or container artifacts, if any are formally released in future +* Dependencies where a vulnerability is caused or materially exposed by this repository (upstream issues should also be reported to the dependency maintainer) + +This repository currently does not establish a production server deployment or +supported release stream. Do not test third-party services or infrastructure +based only on references in this source tree. ==== Out of Scope ❌ @@ -314,9 +297,9 @@ We believe in recognising security researchers who help us improve. ==== Hall of Fame -Researchers who report valid vulnerabilities will be acknowledged in our -link:SECURITY-ACKNOWLEDGMENTS.md[Security Acknowledgments] (unless they -prefer anonymity). +Researchers may be credited in the published security advisory if they +request credit and the advisory platform supports it. No separate public +acknowledgments list is currently maintained. Recognition includes: @@ -325,24 +308,11 @@ Recognition includes: * Brief description of the vulnerability class * Date of report -==== What We Offer - -* ✅ Public credit in security advisories -* ✅ Acknowledgment in release notes -* ✅ Entry in our Hall of Fame -* ✅ Reference/recommendation letter upon request (for significant -findings) +==== Recognition and Compensation -==== What We Don’t Currently Offer - -* ❌ Monetary bug bounties -* ❌ Hardware or swag -* ❌ Paid security research contracts - -____ -*Note:* We’re a community project with limited resources. Your -contributions help everyone who uses this software. -____ +There is no paid bug-bounty program or separately maintained Hall of Fame. +Public attribution is by prior request and only through a published advisory +when possible. No material or timeline for recognition is guaranteed. ''''' @@ -350,66 +320,49 @@ ____ ==== Receiving Updates -To stay informed about security updates: - -* *Watch this repository*: Click "`Watch`" → "`Custom`" → Select -"`Security alerts`" -* *GitHub Security Advisories*: Published at +Follow repository notifications and the https://github.com/hyperpolymath/proven-servers/security/advisories[Security -Advisories] -* *Release notes*: Security fixes noted in link:CHANGELOG.md[CHANGELOG] +Advisories] page. Security changes may be described in +link:CHANGELOG.adoc[CHANGELOG.adoc] when an entry is appropriate. ==== Update Policy -[cols=",",options="header",] -|=== -|Severity |Response -|*Critical/High* |Patch release as soon as fix is ready -|*Medium* |Included in next scheduled release (or earlier) -|*Low* |Included in next scheduled release -|=== +There is no scheduled release cadence or guaranteed security-fix backport +service. Severity and response order are determined case by case; any published +advisory will identify affected revisions and available fixes where known. -==== Supported Versions +==== Supported Revisions -[cols=",,",options="header",] -|=== -|Version |Supported |Notes -|`+main+` branch |✅ Yes |Latest development -|Latest release |✅ Yes |Current stable -|Previous minor release |✅ Yes |Security fixes backported -|Older versions |❌ No |Please upgrade -|=== +No production release or stable-version support matrix is currently declared. +Reports concerning the public source branch may be submitted, but a successful +source check does not imply that a component is safe to deploy. ''''' === Security Best Practices -When using proven-servers, we recommend: +When inspecting or experimenting with this source tree: ==== General -* Keep dependencies up to date -* Use the latest stable release -* Subscribe to security notifications -* Review configuration against security documentation -* Follow principle of least privilege +* Do not use these prototypes as production services or cryptographic implementations. +* Review each package's limitations and dependencies before experimentation. +* Follow repository security notifications if you want to receive advisory updates. ==== For Contributors -* Never commit secrets, credentials, or API keys -* Use signed commits (`+git config commit.gpgsign true+`) -* Review dependencies before adding them -* Run security linters locally before pushing -* Report any concerns about existing code +* Never commit secrets, credentials, or API keys. +* Review dependencies before adding them. +* Run the configured checks and report exactly which checks were unavailable. +* Commit signing is not required by this repository's current policy. ''''' === Additional Resources -* https://github.com/hyperpolymath.gpg[Our PGP Public Key] * https://github.com/hyperpolymath/proven-servers/security/advisories[Security Advisories] -* link:CHANGELOG.md[Changelog] +* link:CHANGELOG.adoc[Changelog] * link:.github/CONTRIBUTING.md[Contributing Guidelines] * https://cve.mitre.org/[CVE Database] * https://www.first.org/cvss/calculator/3.1[CVSS Calculator] @@ -422,14 +375,13 @@ Advisories] |=== |Purpose |Contact |*Security issues* -|https://github.com/hyperpolymath/proven-servers/security/advisories/new[Report -via GitHub] or j.d.a.jewell@open.ac.uk +|https://github.com/hyperpolymath/proven-servers/security/advisories/new[Private GitHub advisory] or j.d.a.jewell@open.ac.uk (no OpenPGP key is currently published) |*General questions* |https://github.com/hyperpolymath/proven-servers/discussions[GitHub Discussions] -|*Other enquiries* |See link:README.md[README] for contact information +|*Other enquiries* |See link:README.adoc[README] for contact information |=== ''''' diff --git a/TEST-NEEDS.adoc b/TEST-NEEDS.adoc index cdb805bb..f7bae7e8 100644 --- a/TEST-NEEDS.adoc +++ b/TEST-NEEDS.adoc @@ -1,158 +1,83 @@ -== TEST-NEEDS.md — proven-servers - -=== CRG Grade: C — ACHIEVED 2026-04-04 - -____ -Generated 2026-03-29 by punishing audit. Updated 2026-04-04 with -property-based and security aspect tests. -____ - -=== Current State - -[width="100%",cols="58%,21%,21%",options="header",] -|=== -|Category |Count |Notes -|Unit tests |~100 |Zig integration tests per protocol (~84 protocols, -most have integration_test.zig). Some protocols have additional focused -tests (amqp_test, dns_test, ftp_test, graphql_test, grpc_test, ca_test, -agentic_test) - -|Integration |1 |tests/cross_binding_test.sh - -|E2E |1 |tests/e2e.sh — 198 lines, covers connector FFI build+test, -protocol FFI sample, core primitives, cross-binding, safety aspects - -|Property-based |1 |tests/property_test.sh — 9 properties (P1–P9): FSM -invalid-transition rejection, initial-transition acceptance, enum tag -counts, slot exhaustion, ABI version integrity, boolean predicate -purity, build compilation, quiescence, slot guard presence - -|Aspect (security) |1 |tests/aspect/security_test.sh — 10 security -aspects (SA1–SA10): state-machine bypass, buffer overflow prevention, -auth spoofing, invalid slot safety, @panic absence, Idris2 dangerous -patterns, mutex protection, max-length constants, hardcoded credentials, -SPDX headers - -|Benchmarks |1 |bindings/rust/benches/protocols.rs — REAL, comprehensive -(tag roundtrip, state machine validation, domain validation, -classification helpers, frame construction) -|=== - -*Source modules:* ~1821 across 84 protocols (Idris2 ABI + Zig FFI each), -5 core modules (frame, fsm, compose, audit, cli), 6 connectors, bindings -(Rust, others). Each protocol has Types.idr + Foreign.idr + main.zig -minimum. - -=== What’s Missing - -==== P2P (Property-Based) Tests - -* [x] State machine transitions: property tests (tests/property_test.sh -— P1, P2, P8) -* [x] Type encoding roundtrip: enum tag count verification -(tests/property_test.sh — P3) -* [x] Slot exhaustion and guard invariants (tests/property_test.sh — P4, -P9) -* [x] ABI version integrity and boolean predicate purity -(tests/property_test.sh — P5, P6) -* [ ] Cross-protocol: property tests for protocol composition invariants -(remaining) - -==== E2E Tests - -* [ ] Per-protocol: for at least the top 20 protocols, full lifecycle -(connect -> handshake -> operate -> disconnect) -* [ ] proven-compose: compose 2+ protocols and verify combined behavior -* [ ] proven-audit: generate audit trail for protocol operations, verify -completeness -* [ ] proven-cli: all CLI commands execute against mock servers - -==== Aspect Tests - -* *Security:* [x] tests/aspect/security_test.sh — SA1 state machine -bypass, SA2 buffer overflow, SA3 auth spoofing, SA4 invalid slot safety, -SA5 @panic absence, SA6 Idris2 dangerous patterns, SA7 mutex protection, -SA8 max-length constants, SA9 hardcoded credentials, SA10 SPDX headers -* *Performance:* Rust benchmarks exist and are REAL (tag roundtrip, -state machine, domain validation). Missing: Zig FFI overhead per -protocol, connection setup latency, throughput under load -* *Concurrency:* No tests for concurrent protocol connections, -connection pool exhaustion, state machine race conditions -* *Error handling:* No tests for malformed protocol messages, connection -drops, timeout handling, invalid state transitions - -==== Build & Execution - -* [ ] Zig build + test for all 84 protocols -* [ ] Idris2 compilation of all protocol ABI specs -* [ ] Rust cargo bench -* [ ] Cross-binding test execution - -==== Benchmarks Needed (Existing + Missing) - -* [x] Tag roundtrip latency (EXISTS, real) -* [x] State machine validation (EXISTS, real) -* [x] Domain validation (EXISTS, real) -* [x] Classification helpers (EXISTS, real) -* [x] Frame construction (EXISTS, real) -* [ ] Per-protocol connection setup time -* [ ] Zig FFI call overhead per protocol -* [ ] Memory usage per active connection -* [ ] Throughput under concurrent connections - -==== Self-Tests - -* [ ] All 84 protocol ABI versions agree between Idris2 and Zig -* [ ] State machine completeness: every valid transition is reachable -* [ ] Audit trail integrity verification -* [ ] Protocol compliance self-check (RFC conformance) - -==== COVERAGE ANALYSIS - -The per-protocol Zig integration tests are REAL and protocol-specific -(verified: AMQP, DNS, Cache all have different content). Each test -covers: ABI version, enum encoding, context lifecycle, transition table, -invalid slot safety. - -However: 84 protocols x ~3-5 test functions each = ~300-400 tests. This -sounds impressive but each test only covers the FFI seam — the Idris2 -formal specifications (752 files) and the protocol logic itself are -tested only by type-checking. - -[width="100%",cols="22%,25%,25%,28%",options="header",] +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) 2026 Jonathan D.A. Jewell += Test Scope and Evidence Needs — proven-servers +:toc: +:sectnums: +:revdate: 2026-09-27 + +[IMPORTANT] +==== +This document records configured checks and missing evidence, not test results. +In the assessment workspace, Just, Idris2, and Zig were unavailable; no +compiler-backed suite was run on this checkout. +==== + +== Configured check entry points + +[cols="2,3,3",options="header"] |=== -|Area |Files |Tests |Status -|Protocol Idris2 ABI |752 |0 unit (type-checked) |Type-check only - -|Protocol Zig FFI |425 |~100 integration |*24% by file* - -|Property invariants |all |9 properties, ~60 assertions -|tests/property_test.sh - -|Security aspects |all |10 aspects, cross-protocol -|tests/aspect/security_test.sh - -|Core modules |5 dirs |5 tests |~1 per core - -|Connectors |6 |0 |*Untested* - -|Rust bindings |111 |1 bench |Bench only +|Command |What it attempts |What it does not establish + +|`just build-idris` +|Build every discovered `.ipkg` under `protocols/`, `core/`, and `connectors/`. +|No independent Zig/C/binding conformance or protocol interoperability. + +|`just build-zig` +|Build every discovered maintained `build.zig` under `protocols/`, `core/`, and +`connectors/`, plus the root FFI example. +|No Idris2 proof status, full runtime behavior, or complete protocol +implementation. + +|`just test-zig` +|Run every discovered Zig `test` target under the maintained package trees. +|No exhaustive network end-to-end or cross-language ABI coverage. + +|`just test-static` +|Run selected source-pattern checks, static security heuristics, and the +binding-directory/source-policy inventory. +|No runtime execution of bindings, compiler proof, security certification, +protocol conformance, or performance measurement. + +|`just e2e` / `bash tests/e2e.sh` +|Build two selected Idris2 package examples, test all discovered core/connector +Zig targets, and test a bounded explicit protocol sample. +|Despite the historical filename, this is not a full network-service E2E or +cross-language conformance suite. + +|`bash tests/source_smoke_test.sh` +|Inspect selected Zig source text for expected patterns. +|Not property-based testing, executable testing, or proof evidence. + +|`bash tests/aspect/security_test.sh` +|Inspect selected source patterns and configuration heuristics. +|Not penetration testing, exploit verification, or a security audit. + +|`bash tests/binding_inventory.sh` +|Report language-directory source counts and run the binding-policy check. +|Does not compile, link, execute, or establish support for a language binding. |=== -=== Priority - -*HIGH.* 1821 source files with ~100 Zig integration tests and 1 -excellent Rust benchmark suite. The per-protocol FFI tests are genuine -and cover the critical seam. The Idris2 type-checking provides formal -guarantees for specifications. BUT: 6 connectors are untested, no E2E -tests for any protocol, no security tests for 84 network protocol -implementations, and no concurrency tests. The Rust benchmark is a model -— extend its pattern to all protocols. - -=== FAKE-FUZZ ALERT - -* `+tests/fuzz/placeholder.txt+` is a scorecard placeholder inherited -from rsr-template-repo — it does NOT provide real fuzz testing -* Replace with an actual fuzz harness (see -rsr-template-repo/tests/fuzz/README.adoc) or remove the file -* Priority: P2 — creates false impression of fuzz coverage +== Evidence still needed + +* Run the full configured Idris2 and Zig build/test matrix on a named revision; + record tool versions, commands, logs, and failures. +* Add per-package executable tests for protocol parsing, encoding, error paths, + state transitions, resource limits, and security-sensitive behavior. +* Establish generated-header, symbol, layout, ownership, and calling-convention + correspondence for each claimed FFI boundary. +* Compile, link, and execute each binding against the intended native library + before making support claims. +* Use independent protocol fixtures or peer implementations to assess wire + behavior; package builds alone are not conformance tests. +* Use vetted test vectors and independent review before claiming cryptographic + behavior. +* Add a benchmark harness only when it has a real package target, reproducible + configuration, and recorded measurements. The presence of + `bindings/rust/benches/protocols.rs` alone does not establish that the source + compiles or that benchmark results exist. +* No fuzz harness is currently configured. Do not count an empty placeholder + or a static test script as fuzz coverage. + +See `READINESS.adoc` and `PROOF-NEEDS.adoc` for the current status and the +specific work needed before stronger correctness, support, or deployment +claims are justified. diff --git a/TOPOLOGY.adoc b/TOPOLOGY.adoc index addc2d83..01142628 100644 --- a/TOPOLOGY.adoc +++ b/TOPOLOGY.adoc @@ -1,127 +1,62 @@ -== proven-servers — Project Topology - -=== System Architecture - -.... - ┌─────────────────────────────────────────────────────────┐ - │ PROVEN-SERVERS │ - │ 108 Formally Verified Server Components │ - └───────────────────────┬─────────────────────────────────┘ - │ - ┌─────────────────────────┼─────────────────────────┐ - │ │ │ - ▼ ▼ ▼ - ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ - │ CORE PRIMITIVES │ │ PROTOCOLS │ │ CONNECTORS │ - │ (8 components) │ │ (94 skeletons) │ │ (6 interfaces) │ - │ │ │ │ │ │ - │ socket frame │ │ dns smtp httpd │ │ dbconn authconn│ - │ fsm wire │ │ mqtt bgp tls │ │ cacheconn queueconn│ - │ compose tls │ │ irc ldap ftp │ │ resolverconn │ - │ config audit │ │ ... (91 more) │ │ storageconn │ - └─────────┬─────────┘ └────────┬───────────┘ └────────┬──────────┘ - │ │ │ - └──────────────────────┼─────────────────────────┘ - │ - ▼ - ┌─────────────────────────────────────────────────────────┐ - │ ABI-FFI LAYER │ - │ │ - │ ┌─────────────┐ ┌────────────┐ ┌─────────────────┐ │ - │ │ Idris2 ABI │ │ C Headers │ │ Zig FFI │ │ - │ │ (proofs) │──│ (generated) │──│ (runtime) │ │ - │ │ │ │ │ │ │ │ - │ │ Layout │ │ tag #defs │ │ enum(u8) │ │ - │ │ Transitions│ │ opaques │ │ handle structs │ │ - │ │ Foreign │ │ fn decls │ │ callconv(.c) │ │ - │ └─────────────┘ └────────────┘ └─────────────────┘ │ - └───────────────────────┬─────────────────────────────────┘ - │ - ▼ - ┌─────────────────────────────────────────────────────────┐ - │ LANGUAGE BINDINGS │ - │ Rust · ReScript · Gleam · Elixir · Haskell · OCaml │ - │ Ada · Julia · C/Zig (free) · (any C-ABI language) │ - └───────────────────────┬─────────────────────────────────┘ - │ - ▼ - ┌─────────────────────────────────────────────────────────┐ - │ CONSUMERS │ - │ PanLL VAB · stapeln containers · user applications │ - └─────────────────────────────────────────────────────────┘ -.... - -=== Completion Dashboard - -.... -COMPONENT STATUS NOTES -───────────────────────────────────── ────────────────── ────────────────────────────── - -PROTOCOL SKELETONS (94) - Type definitions (Types.idr) ██████████ 100% All 94 protocols have types - State machines (Idris2 proofs) ░░░░░░░░░░ 0% Not yet started - ABI-FFI (Zig + C headers) ░░░░░░░░░░ 0% Not yet started - Language bindings ░░░░░░░░░░ 0% Not yet started - -CORE PRIMITIVES (8) - Type definitions (Types.idr) ██████████ 100% socket, frame, fsm, wire, - compose, tls, config, audit - State machines (Idris2 proofs) ░░░░░░░░░░ 0% Not yet started - ABI-FFI (Zig + C headers) ░░░░░░░░░░ 0% Not yet started - Language bindings ░░░░░░░░░░ 0% Not yet started - -CONNECTORS (6) - Type definitions (Types.idr) ██████████ 100% dbconn, authconn, cacheconn, - queueconn, resolverconn, - storageconn - ABI: Layout.idr (tag encodings) ██████████ 100% All 6 — roundtrip proofs ✓ - ABI: Transitions.idr (state machines) ██████████ 100% All 6 — GADTs + witnesses ✓ - ABI: Foreign.idr (FFI contract) ██████████ 100% All 6 — opaque handles ✓ - C Headers (generated) ██████████ 100% All 6 — tag #defines + decls - Zig FFI (runtime enforcement) ██████████ 100% All 6 — build clean ✓ - Zig Tests (integration) ██████████ 100% All 6 — 76+ tests pass ✓ - Language bindings ░░░░░░░░░░ 0% Not yet started - -INFRASTRUCTURE - Repository scaffolding (RSR) ██████████ 100% Justfile, CI, governance - Documentation ██████████ 100% Design docs, READMEs, TOPOLOGY - Machine-readable metadata ██████████ 100% STATE, ECOSYSTEM, META (.a2ml) - CI/CD (Zig builds) ░░░░░░░░░░ 0% Not yet in GitHub Actions - CI/CD (Idris2 type-checking) ░░░░░░░░░░ 0% Not yet in GitHub Actions - -───────────────────────────────────────────────────────────────────────────────────────── -OVERALL: ███░░░░░░░ 35% Connector ABI-FFI complete; - core and protocols pending -.... - -=== Key Dependencies - -.... -Idris2 compiler ───► ABI definitions ───► C header generation ───► Zig FFI - │ │ │ │ - ▼ ▼ ▼ ▼ - Type proofs Tag encodings #define tags enum(u8) types - State machines Roundtrip proofs Opaque structs Handle structs - Capabilities Impossibility proofs Function decls callconv(.c) fns - │ - ▼ - Language bindings - (Rust, ReScript, etc.) - │ - ▼ - PanLL VAB panel - (visual composition) -.... - -=== Update Protocol - -This file is maintained by both humans and AI agents. When updating: - -[arabic] -. *After completing a component*: Change its bar and percentage -. *After adding a component*: Add a new row in the appropriate section -. *After architectural changes*: Update the ASCII diagram -. *Date*: Update the `+Last updated+` comment at the top of this file - -Progress bars use: `+█+` (filled) and `+░+` (empty), 10 characters wide. -Percentages: 0%, 10%, 20%, … 100% (in 10% increments). +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += proven-servers — Repository Topology and Inventory + +:revdate: 2026-09-27 +:toc: + +[IMPORTANT] +==== +This diagram describes source-tree organization, not implementation readiness. +It does not imply that packages build, that an FFI conforms to an Idris model, +or that any directory contains a production server. +==== + +== Repository map + +[source] +---- +proven-servers/ +├── protocols/ 88 protocol-named package directories +│ └── each package may contain Idris2 model, Zig FFI, tests, headers +├── core/ 5 package directories: audit, cli, compose, frame, fsm +├── connectors/ 6 package directories, including nesy-solver-api +├── bindings/ 20 language-named directories; readiness varies +├── not-proven/ explicitly non-proven examples/prototypes +├── tests/ partial integration and static source checks +├── audits/ dated historical audit records +├── src/abi/ root-level Idris2 source +├── ffi/zig/ root-level Zig sources +├── generated/abi/ headers/ABI artifacts; generation status varies +└── .machine_readable/ state, policy, registries, agent guidance +---- + +== Implementation and evidence boundary + +The repository has no single uniform ABI/FFI layer covering every package. +Package layout, generated headers, build files, and tests vary. Idris2 source +can state model-level invariants; Zig code and language bindings are separate +implementations whose correspondence requires package-specific build and +conformance evidence. + +Some operations deliberately reject or raise an unavailable error rather than +pretend a backend exists. In particular, the OCaml raw-symbol bridge is disabled +pending OCaml-compatible C stubs. See the relevant package README and +`READINESS.adoc` before relying on a specific component. + +== Inventory counts + +Counts are derived from immediate package directories at the time of writing: + +[cols="1,1,2"] +|=== +|Area |Count |Interpretation + +|Protocols |88 |Directory inventory only. +|Core |5 |`audit`, `cli`, `compose`, `frame`, `fsm`. +|Connectors |6 |Includes `nesy-solver-api`; not six equivalent connector ABIs. +|Bindings |20 |Language-named directories, not twenty verified bindings. +|=== + +Update counts only from the source tree, and keep them explicitly separate from +build, test, proof, or release claims. diff --git a/benches/.gitkeep b/benches/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/bindings/ada/src/proven_dns.ads b/bindings/ada/src/proven_dns.ads index 19af06a6..75664382 100644 --- a/bindings/ada/src/proven_dns.ads +++ b/bindings/ada/src/proven_dns.ads @@ -2,7 +2,9 @@ -- (MPL-2.0 preferred; MPL-2.0 required for GNAT ecosystem) -- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -- --- Ada bindings for the proven-dns protocol (DNS server). +-- Ada bindings for the bounded proven-dns message-builder FFI. +-- It accepts only exact 17-byte standard queries with one root-name question; +-- responses are capped at 512 bytes. DNSSEC cryptographic operations fail closed. -- -- Wraps the C-ABI functions from protocols/proven-dns/ffi/zig/src/dns.zig: -- dns_abi_version, dns_create_context, dns_destroy_context, @@ -38,19 +40,17 @@ package Proven_Dns is State_Sent => 4); pragma Convention (C, Dns_State); - -- DNSSEC processing states. + -- DNSSEC ABI/model tags; operational key loading, signing, and validation fail closed. type Dnssec_State is (Dnssec_Disabled, Dnssec_Enabled, Dnssec_Key_Loaded, - Dnssec_Signed, Dnssec_Validated); for Dnssec_State use (Dnssec_Disabled => 0, Dnssec_Enabled => 1, Dnssec_Key_Loaded => 2, - Dnssec_Signed => 3, - Dnssec_Validated => 4); + Dnssec_Validated => 3); pragma Convention (C, Dnssec_State); -- DNS record types (subset). @@ -135,6 +135,7 @@ package Proven_Dns is function Query_Class (Slot : int) return unsigned_char; pragma Import (C, Query_Class, "dns_query_class"); + -- Caller must provide a valid buffer of exactly 17 bytes; only the root-question subset is accepted. function Parse_Query (Slot : int; Buf : access unsigned_char; @@ -147,6 +148,7 @@ package Proven_Dns is function Begin_Response (Slot : int) return unsigned_char; pragma Import (C, Begin_Response, "dns_begin_response"); + -- RDATA length must be <=256; Rdata must be valid/non-null if Rdlen > 0. function Add_Answer (Slot : int; Rtype : unsigned_char; @@ -156,6 +158,7 @@ package Proven_Dns is Rdlen : unsigned_short) return unsigned_char; pragma Import (C, Add_Answer, "dns_add_answer"); + -- RDATA length must be <=256; Rdata must be valid/non-null if Rdlen > 0. function Add_Authority (Slot : int; Rtype : unsigned_char; @@ -165,6 +168,7 @@ package Proven_Dns is Rdlen : unsigned_short) return unsigned_char; pragma Import (C, Add_Authority, "dns_add_authority"); + -- RDATA length must be <=256; Rdata must be valid/non-null if Rdlen > 0. function Add_Additional (Slot : int; Rtype : unsigned_char; @@ -179,23 +183,29 @@ package Proven_Dns is Rcode_Tag : unsigned_char) return unsigned_char; pragma Import (C, Set_Rcode, "dns_set_rcode"); + -- Raw pointer contract: Out_Buf must reference at least 512 writable bytes. + -- The ABI has no capacity argument; messages over 512 bytes are rejected before writing. function Build_Response (Slot : int; Out_Buf : access unsigned_char; Out_Len : access unsigned_short) return unsigned_char; pragma Import (C, Build_Response, "dns_build_response"); + -- Enable mode only; response construction then rejects because no signer exists. function Enable_Dnssec (Slot : int) return unsigned_char; pragma Import (C, Enable_Dnssec, "dns_enable_dnssec"); + -- Always rejects: this ABI accepts an algorithm tag but no private-key material. function Load_Dnssec_Key (Slot : int; Algo : unsigned_char) return unsigned_char; pragma Import (C, Load_Dnssec_Key, "dns_load_dnssec_key"); + -- Always rejects: no DNSSEC signing backend is present. function Sign_Response (Slot : int) return unsigned_char; pragma Import (C, Sign_Response, "dns_sign_response"); + -- Always rejects: no DNSSEC validator is present. function Validate_Dnssec (Slot : int) return unsigned_char; pragma Import (C, Validate_Dnssec, "dns_validate_dnssec"); diff --git a/bindings/cpp/include/proven/dns.hpp b/bindings/cpp/include/proven/dns.hpp index 90c0019f..7bc84362 100644 --- a/bindings/cpp/include/proven/dns.hpp +++ b/bindings/cpp/include/proven/dns.hpp @@ -2,18 +2,21 @@ // Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) // /// @file dns.hpp -/// @brief C++ bindings for proven-dns (DNS server protocol). +/// @brief C++ bindings for the bounded proven-dns message-builder FFI. /// -/// RAII wrapper around the Zig FFI context pool. Lifecycle: -/// Idle -> QueryReceived -> Lookup -> ResponseBuilding -> Sent. -/// DNSSEC sub-state: Disabled -> Enabled -> KeyLoaded -> Validated. +/// Bounded root-question message-builder wrapper around the Zig FFI context pool. +/// It accepts only the exact 17-byte standard-query subset and emits at most +/// 512 bytes; this is not a general resolver. DNSSEC key loading/signing/ +/// validation fail closed; DNSSEC states below are ABI/model tags only. #ifndef PROVEN_DNS_HPP #define PROVEN_DNS_HPP #include "error.hpp" #include +#include #include +#include extern "C" { uint32_t dns_abi_version(); @@ -27,6 +30,7 @@ extern "C" { uint16_t dns_additional_count(int slot); uint8_t dns_query_rtype(int slot); uint8_t dns_query_class(int slot); + // Raw FFI: accepts only the exact 17-byte standard root-question subset. uint8_t dns_parse_query(int slot, const uint8_t* buf, uint16_t len); uint8_t dns_begin_lookup(int slot); uint8_t dns_begin_response(int slot); @@ -34,6 +38,7 @@ extern "C" { uint8_t dns_add_authority(int slot, uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t* rdata, uint16_t rdlen); uint8_t dns_add_additional(int slot, uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t* rdata, uint16_t rdlen); uint8_t dns_set_rcode(int slot, uint8_t rcode_tag); + // Raw FFI: out must reference at least 512 writable bytes; no capacity argument exists. uint8_t dns_build_response(int slot, uint8_t* out, uint16_t* out_len); uint8_t dns_enable_dnssec(int slot); uint8_t dns_load_dnssec_key(int slot, uint8_t algo); @@ -50,7 +55,7 @@ enum class DnsState : uint8_t { Idle = 0, QueryReceived = 1, Lookup = 2, ResponseBuilding = 3, Sent = 4 }; -/// @brief DNSSEC sub-state machine. +/// @brief DNSSEC ABI/model state tags; cryptographic operations are unavailable. enum class DnssecState : uint8_t { Disabled = 0, Enabled = 1, KeyLoaded = 2, Validated = 3 }; @@ -93,38 +98,52 @@ class DnsContext { [[nodiscard]] uint8_t query_rtype() const { return dns_query_rtype(slot_); } [[nodiscard]] uint8_t query_class() const { return dns_query_class(slot_); } - /// @brief Parse a DNS query. Transitions Idle -> QueryReceived. - void parse_query(const uint8_t* data, uint16_t len) { - ProvenError::check_status(dns_parse_query(slot_, data, len)); + /// @brief Parse only the exact 17-byte standard root-question query subset. + void parse_query(const uint8_t* data, std::size_t len) { + if (data == nullptr || len != 17) { + throw std::invalid_argument("DNS query must be a non-null 17-byte buffer"); + } + ProvenError::check_status(dns_parse_query(slot_, data, static_cast(len))); } void begin_lookup() { ProvenError::check_status(dns_begin_lookup(slot_)); } void begin_response() { ProvenError::check_status(dns_begin_response(slot_)); } - void add_answer(uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t* rdata, uint16_t rdlen) { - ProvenError::check_status(dns_add_answer(slot_, rtype, rclass, ttl, rdata, rdlen)); + void add_answer(uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t* rdata, std::size_t rdlen) { + check_rdata(rdata, rdlen); + ProvenError::check_status(dns_add_answer(slot_, rtype, rclass, ttl, rdata, static_cast(rdlen))); } - void add_authority(uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t* rdata, uint16_t rdlen) { - ProvenError::check_status(dns_add_authority(slot_, rtype, rclass, ttl, rdata, rdlen)); + void add_authority(uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t* rdata, std::size_t rdlen) { + check_rdata(rdata, rdlen); + ProvenError::check_status(dns_add_authority(slot_, rtype, rclass, ttl, rdata, static_cast(rdlen))); } - void add_additional(uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t* rdata, uint16_t rdlen) { - ProvenError::check_status(dns_add_additional(slot_, rtype, rclass, ttl, rdata, rdlen)); + void add_additional(uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t* rdata, std::size_t rdlen) { + check_rdata(rdata, rdlen); + ProvenError::check_status(dns_add_additional(slot_, rtype, rclass, ttl, rdata, static_cast(rdlen))); } void set_rcode(uint8_t rcode_tag) { ProvenError::check_status(dns_set_rcode(slot_, rcode_tag)); } - /// @brief Build response. Returns bytes written to out. - uint16_t build_response(uint8_t* out) { + /// @brief Build response. Requires an output buffer of at least 512 bytes. + /// @return Number of bytes written (at most 512). + uint16_t build_response(uint8_t* out, std::size_t capacity) { + if (out == nullptr || capacity < 512) { + throw std::invalid_argument("DNS response buffer must be at least 512 bytes"); + } uint16_t out_len = 0; ProvenError::check_status(dns_build_response(slot_, out, &out_len)); return out_len; } + /// Enable mode only; response construction then rejects without a signer. void enable_dnssec() { ProvenError::check_status(dns_enable_dnssec(slot_)); } + /// Always fails closed: the ABI accepts no private-key material. void load_dnssec_key(DnssecAlgorithm algo) { ProvenError::check_status(dns_load_dnssec_key(slot_, static_cast(algo))); } + /// Always fails closed because no DNSSEC signing backend exists. void sign_response() { ProvenError::check_status(dns_sign_response(slot_)); } + /// Always returns false because no DNSSEC validator exists. [[nodiscard]] bool validate_dnssec() const { return dns_validate_dnssec(slot_) == 0; } static bool can_transition(DnsState from, DnsState to) { @@ -138,6 +157,12 @@ class DnsContext { static uint32_t abi_version() { return dns_abi_version(); } private: + static void check_rdata(const uint8_t* data, std::size_t len) { + if (len > 256 || (len > 0 && data == nullptr)) { + throw std::invalid_argument("DNS RDATA must be at most 256 bytes and non-null when non-empty"); + } + } + int slot_; }; diff --git a/bindings/csharp/src/ProvenDns.cs b/bindings/csharp/src/ProvenDns.cs index 9f1a7a15..4c481286 100644 --- a/bindings/csharp/src/ProvenDns.cs +++ b/bindings/csharp/src/ProvenDns.cs @@ -1,8 +1,9 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) // -// C# P/Invoke bindings for the proven-dns protocol. -// Wraps the C-ABI functions from protocols/proven-dns/ffi/zig/src/dns.zig. +// C# P/Invoke bindings for the bounded proven-dns message-builder FFI. +// Only exact 17-byte standard queries with one root-name question are accepted; +// responses are capped at 512 bytes. DNSSEC cryptographic operations fail closed. using System; using System.Runtime.InteropServices; @@ -29,8 +30,8 @@ public enum DnssecAlgorithm : byte } /// - /// C# bindings for the proven DNS server protocol. - /// Lifecycle: Idle -> QueryReceived -> Lookup -> ResponseBuilding -> Sent. + /// C# bindings for the bounded DNS message-builder model. + /// It is not a general resolver; DNSSEC key loading, signing, and validation fail closed. /// public static class ProvenDns { @@ -88,9 +89,20 @@ public static class ProvenDns public static byte QueryRtype(int slot) => dns_query_rtype(slot); public static byte QueryClass(int slot) => dns_query_class(slot); - /// Parse a DNS query. Transitions Idle -> QueryReceived. - public static void ParseQuery(int slot, byte[] data) => - ProvenError.CheckStatus(dns_parse_query(slot, data, (ushort)data.Length)); + private static ushort CheckedRdataLength(byte[] rdata) + { + if (rdata is null || rdata.Length > 256) + throw new ArgumentException("DNS RDATA must be at most 256 bytes", nameof(rdata)); + return (ushort)rdata.Length; + } + + /// Parse only the exact 17-byte standard root-question query subset. + public static void ParseQuery(int slot, byte[] data) + { + if (data is null || data.Length != 17) + throw new ArgumentException("DNS query must be exactly 17 bytes", nameof(data)); + ProvenError.CheckStatus(dns_parse_query(slot, data, 17)); + } /// Begin lookup. Transitions QueryReceived -> Lookup. public static void BeginLookup(int slot) => @@ -100,17 +112,17 @@ public static void BeginLookup(int slot) => public static void BeginResponse(int slot) => ProvenError.CheckStatus(dns_begin_response(slot)); - /// Add a resource record to the answer section. + /// Add an answer record; RDATA is limited to 256 bytes. public static void AddAnswer(int slot, byte rtype, byte rclass, uint ttl, byte[] rdata) => - ProvenError.CheckStatus(dns_add_answer(slot, rtype, rclass, ttl, rdata, (ushort)rdata.Length)); + ProvenError.CheckStatus(dns_add_answer(slot, rtype, rclass, ttl, rdata, CheckedRdataLength(rdata))); - /// Add a resource record to the authority section. + /// Add an authority record; RDATA is limited to 256 bytes. public static void AddAuthority(int slot, byte rtype, byte rclass, uint ttl, byte[] rdata) => - ProvenError.CheckStatus(dns_add_authority(slot, rtype, rclass, ttl, rdata, (ushort)rdata.Length)); + ProvenError.CheckStatus(dns_add_authority(slot, rtype, rclass, ttl, rdata, CheckedRdataLength(rdata))); - /// Add a resource record to the additional section. + /// Add an additional record; RDATA is limited to 256 bytes. public static void AddAdditional(int slot, byte rtype, byte rclass, uint ttl, byte[] rdata) => - ProvenError.CheckStatus(dns_add_additional(slot, rtype, rclass, ttl, rdata, (ushort)rdata.Length)); + ProvenError.CheckStatus(dns_add_additional(slot, rtype, rclass, ttl, rdata, CheckedRdataLength(rdata))); /// Set the response code. public static void SetRcode(int slot, byte rcodeTag) => @@ -119,24 +131,26 @@ public static void SetRcode(int slot, byte rcodeTag) => /// Build the DNS response. Returns bytes written to outBuf. public static ushort BuildResponse(int slot, byte[] outBuf) { + if (outBuf is null || outBuf.Length < 512) + throw new System.ArgumentException("DNS response buffer must be at least 512 bytes", nameof(outBuf)); ushort outLen = 0; ProvenError.CheckStatus(dns_build_response(slot, outBuf, ref outLen)); return outLen; } - /// Enable DNSSEC. Transitions Disabled -> Enabled. + /// Enable mode only; response construction then rejects without a signer. public static void EnableDnssec(int slot) => ProvenError.CheckStatus(dns_enable_dnssec(slot)); - /// Load DNSSEC signing key. Transitions Enabled -> KeyLoaded. + /// Always fails closed: the ABI accepts no private-key material. public static void LoadDnssecKey(int slot, DnssecAlgorithm algo) => ProvenError.CheckStatus(dns_load_dnssec_key(slot, (byte)algo)); - /// Sign the response. Transitions KeyLoaded -> Validated. + /// Always fails closed because no DNSSEC signing backend exists. public static void SignResponse(int slot) => ProvenError.CheckStatus(dns_sign_response(slot)); - /// Check DNSSEC validation result. + /// Always returns false because no DNSSEC validator exists. public static bool ValidateDnssec(int slot) => dns_validate_dnssec(slot) == 0; public static bool CanTransition(DnsState from, DnsState to) => diff --git a/bindings/go/dns.go b/bindings/go/dns.go index 7ed8de9a..83fbdba1 100644 --- a/bindings/go/dns.go +++ b/bindings/go/dns.go @@ -1,8 +1,11 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -// DNS protocol bindings for proven-servers. +// Bounded DNS message-builder bindings for proven-servers. // +// Accepts only exact 17-byte standard queries with one root-name question; +// responses are capped at 512 bytes. This is not a general resolver. DNSSEC +// key loading, signing, and validation fail closed (validation is always false). // Wraps the C-ABI functions from protocols/proven-dns/ffi/zig/src/dns.zig. // Lifecycle: create -> parse_query -> begin_lookup -> begin_response -> // add records -> set_rcode -> build_response -> destroy. @@ -53,14 +56,14 @@ const ( DnsSent // Response sent (terminal) ) -// DnssecState represents the DNSSEC sub-state machine. +// DnssecState represents abstract DNSSEC ABI/model tags, not crypto capability. type DnssecState uint8 const ( DnssecDisabled DnssecState = iota // DNSSEC disabled DnssecEnabled // DNSSEC enabled, no key loaded - DnssecKeyLoaded // DNSSEC key loaded - DnssecValidated // Response validated / signed + DnssecKeyLoaded // ABI/model state; operational key loading is unavailable + DnssecValidated // ABI/model state; no operational DNSSEC validation ) // DnssecAlgorithm represents a DNSSEC signing algorithm. @@ -147,9 +150,9 @@ func (ctx *DnsContext) QueryClass() uint8 { return uint8(C.dns_query_class(ctx.slot)) } -// ParseQuery parses a DNS query from raw bytes. Transitions Idle -> QueryReceived. +// ParseQuery accepts only the exact 17-byte standard root-question subset. func (ctx *DnsContext) ParseQuery(data []byte) error { - if len(data) == 0 { + if len(data) != 17 { return &ProvenError{Code: 0, Kind: ErrInvalidParameter} } return statusError(C.dns_parse_query(ctx.slot, (*C.uint8_t)(unsafe.Pointer(&data[0])), C.uint16_t(len(data)))) @@ -167,6 +170,9 @@ func (ctx *DnsContext) BeginResponse() error { // AddAnswer adds a resource record to the answer section. func (ctx *DnsContext) AddAnswer(rtype, rclass uint8, ttl uint32, rdata []byte) error { + if len(rdata) > 256 { + return &ProvenError{Code: 0, Kind: ErrCapacityExceeded} + } var ptr *C.uint8_t if len(rdata) > 0 { ptr = (*C.uint8_t)(unsafe.Pointer(&rdata[0])) @@ -176,6 +182,9 @@ func (ctx *DnsContext) AddAnswer(rtype, rclass uint8, ttl uint32, rdata []byte) // AddAuthority adds a resource record to the authority section. func (ctx *DnsContext) AddAuthority(rtype, rclass uint8, ttl uint32, rdata []byte) error { + if len(rdata) > 256 { + return &ProvenError{Code: 0, Kind: ErrCapacityExceeded} + } var ptr *C.uint8_t if len(rdata) > 0 { ptr = (*C.uint8_t)(unsafe.Pointer(&rdata[0])) @@ -185,6 +194,9 @@ func (ctx *DnsContext) AddAuthority(rtype, rclass uint8, ttl uint32, rdata []byt // AddAdditional adds a resource record to the additional section. func (ctx *DnsContext) AddAdditional(rtype, rclass uint8, ttl uint32, rdata []byte) error { + if len(rdata) > 256 { + return &ProvenError{Code: 0, Kind: ErrCapacityExceeded} + } var ptr *C.uint8_t if len(rdata) > 0 { ptr = (*C.uint8_t)(unsafe.Pointer(&rdata[0])) @@ -197,7 +209,7 @@ func (ctx *DnsContext) SetRcode(rcodeTag uint8) error { return statusError(C.dns_set_rcode(ctx.slot, C.uint8_t(rcodeTag))) } -// BuildResponse builds the DNS response message. Transitions ResponseBuilding -> Sent. +// BuildResponse builds the bounded response. Transitions ResponseBuilding -> Sent. // The output buffer must be at least 512 bytes. Returns the number of bytes written. func (ctx *DnsContext) BuildResponse(out []byte) (uint16, error) { if len(out) < 512 { @@ -208,22 +220,22 @@ func (ctx *DnsContext) BuildResponse(out []byte) (uint16, error) { return uint16(outLen), err } -// EnableDnssec enables DNSSEC. Transitions Disabled -> Enabled. +// EnableDnssec enables DNSSEC mode only; response construction then rejects without a signer. func (ctx *DnsContext) EnableDnssec() error { return statusError(C.dns_enable_dnssec(ctx.slot)) } -// LoadDnssecKey loads a DNSSEC signing key. Transitions Enabled -> KeyLoaded. +// LoadDnssecKey always fails closed: the ABI has no private-key material. func (ctx *DnsContext) LoadDnssecKey(algo DnssecAlgorithm) error { return statusError(C.dns_load_dnssec_key(ctx.slot, C.uint8_t(algo))) } -// SignResponse signs the response (DNSSEC). Transitions KeyLoaded -> Validated. +// SignResponse always fails closed because no DNSSEC signing backend exists. func (ctx *DnsContext) SignResponse() error { return statusError(C.dns_sign_response(ctx.slot)) } -// ValidateDnssec checks DNSSEC validation. Returns true if validated. +// ValidateDnssec always returns false because no DNSSEC validator exists. func (ctx *DnsContext) ValidateDnssec() bool { return C.dns_validate_dnssec(ctx.slot) == 0 } diff --git a/bindings/java/src/main/java/com/hyperpolymath/proven/ProvenDns.java b/bindings/java/src/main/java/com/hyperpolymath/proven/ProvenDns.java index 90765cfb..eb3282b2 100644 --- a/bindings/java/src/main/java/com/hyperpolymath/proven/ProvenDns.java +++ b/bindings/java/src/main/java/com/hyperpolymath/proven/ProvenDns.java @@ -8,10 +8,12 @@ package com.hyperpolymath.proven; /** - * Java bindings for the proven DNS server protocol. + * Java bindings for the bounded proven DNS message-builder FFI. * - *

Lifecycle: Idle -> QueryReceived -> Lookup -> ResponseBuilding -> Sent. - * Supports DNSSEC signing and validation via a parallel state machine.

+ *

This is a bounded message-builder, not a general resolver: it accepts only + * exact 17-byte standard queries with one root-name question and caps responses + * at 512 bytes. DNSSEC key loading, signing, and validation fail closed; the + * exposed DNSSEC state transitions are ABI/model tags only.

* * @author Jonathan D.A. Jewell */ @@ -136,13 +138,23 @@ public static int createContext() throws ProvenError { public static int queryClass(int slot) { return nativeQueryClass(slot); } + private static int checkedRdataLength(byte[] rdata) { + if (rdata == null || rdata.length > 256) { + throw new IllegalArgumentException("DNS RDATA must be at most 256 bytes"); + } + return rdata.length; + } + /** - * Parse a DNS query. Transitions Idle -> QueryReceived. + * Parse only the exact 17-byte standard root-question query subset. * * @throws ProvenError on parse failure or invalid state */ public static void parseQuery(int slot, byte[] data) throws ProvenError { - ProvenError.checkStatus(nativeParseQuery(slot, data, data.length)); + if (data == null || data.length != 17) { + throw new IllegalArgumentException("DNS query must be exactly 17 bytes"); + } + ProvenError.checkStatus(nativeParseQuery(slot, data, 17)); } /** Begin lookup. Transitions QueryReceived -> Lookup. */ @@ -157,17 +169,17 @@ public static void beginResponse(int slot) throws ProvenError { /** Add a resource record to the answer section. */ public static void addAnswer(int slot, int rtype, int rclass, int ttl, byte[] rdata) throws ProvenError { - ProvenError.checkStatus(nativeAddAnswer(slot, rtype, rclass, ttl, rdata, rdata.length)); + ProvenError.checkStatus(nativeAddAnswer(slot, rtype, rclass, ttl, rdata, checkedRdataLength(rdata))); } /** Add a resource record to the authority section. */ public static void addAuthority(int slot, int rtype, int rclass, int ttl, byte[] rdata) throws ProvenError { - ProvenError.checkStatus(nativeAddAuthority(slot, rtype, rclass, ttl, rdata, rdata.length)); + ProvenError.checkStatus(nativeAddAuthority(slot, rtype, rclass, ttl, rdata, checkedRdataLength(rdata))); } /** Add a resource record to the additional section. */ public static void addAdditional(int slot, int rtype, int rclass, int ttl, byte[] rdata) throws ProvenError { - ProvenError.checkStatus(nativeAddAdditional(slot, rtype, rclass, ttl, rdata, rdata.length)); + ProvenError.checkStatus(nativeAddAdditional(slot, rtype, rclass, ttl, rdata, checkedRdataLength(rdata))); } /** Set the response code. */ @@ -184,27 +196,30 @@ public static void setRcode(int slot, int rcodeTag) throws ProvenError { * @throws ProvenError on failure */ public static int buildResponse(int slot, byte[] outBuf) throws ProvenError { + if (outBuf == null || outBuf.length < 512) { + throw new IllegalArgumentException("DNS response buffer must be at least 512 bytes"); + } int[] outLen = new int[1]; ProvenError.checkStatus(nativeBuildResponse(slot, outBuf, outLen)); return outLen[0]; } - /** Enable DNSSEC. Transitions Disabled -> Enabled. */ + /** Enable mode only; response construction then rejects because no signer exists. */ public static void enableDnssec(int slot) throws ProvenError { ProvenError.checkStatus(nativeEnableDnssec(slot)); } - /** Load a DNSSEC signing key. Transitions Enabled -> KeyLoaded. */ + /** Always fails closed: the ABI accepts no private-key material. */ public static void loadDnssecKey(int slot, DnssecAlgorithm algo) throws ProvenError { ProvenError.checkStatus(nativeLoadDnssecKey(slot, algo.tag())); } - /** Sign the response. Transitions KeyLoaded -> Validated. */ + /** Always fails closed because no DNSSEC signing backend exists. */ public static void signResponse(int slot) throws ProvenError { ProvenError.checkStatus(nativeSignResponse(slot)); } - /** Check DNSSEC validation result. */ + /** Always returns false because no DNSSEC validator exists. */ public static boolean validateDnssec(int slot) { return nativeValidateDnssec(slot) == 0; } diff --git a/bindings/javascript/src/dns.js b/bindings/javascript/src/dns.js index 71580f49..7861f4a9 100644 --- a/bindings/javascript/src/dns.js +++ b/bindings/javascript/src/dns.js @@ -1,7 +1,10 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Jonathan D.A. Jewell // -// JavaScript bindings for the proven-dns Zig FFI. +// Bounded DNS message-builder bindings for the proven-dns Zig FFI. +// Accepts only exact 17-byte standard queries with one root-name question; +// responses are capped at 512 bytes. It is not a general resolver, and DNSSEC +// key loading, signing, and validation fail closed. import { checkSlot, checkStatus } from "./error.js"; import { loadLibrary } from "./ffi.js"; @@ -77,8 +80,11 @@ export class DnsContext { /** @returns {number} */ queryRtype() { return lib().dns_query_rtype(this._slot); } /** @returns {number} */ queryClass() { return lib().dns_query_class(this._slot); } - /** @param {Uint8Array} data */ + /** Parse only the exact 17-byte standard root-question query subset. @param {Uint8Array} data */ parseQuery(data) { + if (!(data instanceof Uint8Array) || data.length !== 17) { + throw new RangeError("DNS query must be a 17-byte Uint8Array"); + } checkStatus(lib().dns_parse_query(this._slot, data, data.length)); } @@ -92,16 +98,25 @@ export class DnsContext { * @param {Uint8Array} rdata */ addAnswer(rtype, rclass, ttl, rdata) { + if (!(rdata instanceof Uint8Array) || rdata.length > 256) { + throw new RangeError("DNS RDATA must be a Uint8Array of at most 256 bytes"); + } checkStatus(lib().dns_add_answer(this._slot, rtype, rclass, ttl, rdata, rdata.length)); } /** @param {number} rtype @param {number} rclass @param {number} ttl @param {Uint8Array} rdata */ addAuthority(rtype, rclass, ttl, rdata) { + if (!(rdata instanceof Uint8Array) || rdata.length > 256) { + throw new RangeError("DNS RDATA must be a Uint8Array of at most 256 bytes"); + } checkStatus(lib().dns_add_authority(this._slot, rtype, rclass, ttl, rdata, rdata.length)); } /** @param {number} rtype @param {number} rclass @param {number} ttl @param {Uint8Array} rdata */ addAdditional(rtype, rclass, ttl, rdata) { + if (!(rdata instanceof Uint8Array) || rdata.length > 256) { + throw new RangeError("DNS RDATA must be a Uint8Array of at most 256 bytes"); + } checkStatus(lib().dns_add_additional(this._slot, rtype, rclass, ttl, rdata, rdata.length)); } @@ -114,20 +129,25 @@ export class DnsContext { * @returns {Uint8Array} Serialized DNS response. */ buildResponse(maxLen = 512) { + if (!Number.isInteger(maxLen) || maxLen < 512) { + throw new RangeError("DNS response buffer must be at least 512 bytes"); + } const buf = new Uint8Array(maxLen); const outLen = new Uint16Array(1); checkStatus(lib().dns_build_response(this._slot, buf, outLen)); return buf.subarray(0, outLen[0]); } + /** Enable mode only; response building then rejects without a signer. */ enableDnssec() { checkStatus(lib().dns_enable_dnssec(this._slot)); } - /** @param {number} algo - DnssecAlgorithm tag. */ + /** Always fails closed: the ABI accepts no private-key material. @param {number} algo */ loadDnssecKey(algo) { checkStatus(lib().dns_load_dnssec_key(this._slot, algo)); } + /** Always fails closed because no DNSSEC signing backend exists. */ signResponse() { checkStatus(lib().dns_sign_response(this._slot)); } - /** @returns {boolean} */ + /** Always returns false because no DNSSEC validator exists. @returns {boolean} */ validateDnssec() { return lib().dns_validate_dnssec(this._slot) === 0; } } diff --git a/bindings/julia/src/Dns.jl b/bindings/julia/src/Dns.jl index ee543554..980dd3ec 100644 --- a/bindings/julia/src/Dns.jl +++ b/bindings/julia/src/Dns.jl @@ -1,7 +1,9 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# Julia bindings for the proven-dns protocol (DNS server). +# Julia bindings for the bounded proven-dns message-builder FFI. +# It accepts exact 17-byte standard root-question queries and caps responses +# at 512 bytes; it is not a general resolver. DNSSEC crypto fails closed. # # Wraps the C-ABI functions from protocols/proven-dns/ffi/zig/src/dns.zig # via ccall into libproven_dns.so. @@ -31,13 +33,12 @@ const LIB = "libproven_dns" STATE_SENT = 4 end -"""DNSSEC processing states.""" +"""DNSSEC ABI/model states; key loading, signing, and validation are unavailable.""" @enum DnssecState::UInt8 begin DNSSEC_DISABLED = 0 DNSSEC_ENABLED = 1 DNSSEC_KEY_LOADED = 2 - DNSSEC_SIGNED = 3 - DNSSEC_VALIDATED = 4 + DNSSEC_VALIDATED = 3 end """DNS response codes.""" @@ -120,9 +121,10 @@ end """ parse_query(slot::SlotId, data::Vector{UInt8}) -Parse a DNS query from raw bytes. Throws on invalid state. +Parse only the exact 17-byte standard query with one root-name question. """ function parse_query(slot::SlotId, data::Vector{UInt8})::Nothing + length(data) == 17 || throw(ArgumentError("DNS query must be exactly 17 bytes")) raw = ccall((:dns_parse_query, LIB), UInt8, (Cint, Ptr{UInt8}, UInt16), slot, data, UInt16(length(data))) @@ -160,7 +162,7 @@ end """ enable_dnssec(slot::SlotId) -Enable DNSSEC for the context. Throws on invalid state. +Enable DNSSEC mode only; response construction then rejects without a signer. """ function enable_dnssec(slot::SlotId)::Nothing check_status(ccall((:dns_enable_dnssec, LIB), UInt8, (Cint,), slot)) @@ -169,7 +171,7 @@ end """ sign_response(slot::SlotId) -Sign the DNS response with DNSSEC. Throws on invalid state. +Always fails closed because no DNSSEC signing backend is available. """ function sign_response(slot::SlotId)::Nothing check_status(ccall((:dns_sign_response, LIB), UInt8, (Cint,), slot)) @@ -178,10 +180,10 @@ end """ validate_dnssec(slot::SlotId) -Validate DNSSEC signatures. Throws on invalid state. +Always returns false because no DNSSEC validator is available. """ -function validate_dnssec(slot::SlotId)::Nothing - check_status(ccall((:dns_validate_dnssec, LIB), UInt8, (Cint,), slot)) +function validate_dnssec(slot::SlotId)::Bool + ccall((:dns_validate_dnssec, LIB), UInt8, (Cint,), slot) == 0x00 end """ @@ -197,7 +199,7 @@ end """ can_dnssec_transition(from::DnssecState, to::DnssecState) -> Bool -Check whether a DNSSEC state transition is valid. +Check the abstract DNSSEC state model only; this does not imply crypto availability. """ function can_dnssec_transition(from::DnssecState, to::DnssecState)::Bool ccall((:dns_can_dnssec_transition, LIB), UInt8, diff --git a/bindings/kotlin/src/main/kotlin/com/hyperpolymath/proven/ProvenDns.kt b/bindings/kotlin/src/main/kotlin/com/hyperpolymath/proven/ProvenDns.kt index b821b760..4dd58371 100644 --- a/bindings/kotlin/src/main/kotlin/com/hyperpolymath/proven/ProvenDns.kt +++ b/bindings/kotlin/src/main/kotlin/com/hyperpolymath/proven/ProvenDns.kt @@ -1,16 +1,18 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) // -// Kotlin/JNI bindings for the proven-dns protocol. -// Wraps the C-ABI functions from protocols/proven-dns/ffi/zig/src/dns.zig. +// Kotlin/JNI bindings for the bounded proven-dns message-builder FFI. +// Only exact 17-byte standard queries with one root-name question are accepted; +// responses are capped at 512 bytes. DNSSEC cryptographic operations fail closed. // Enum classes match Idris2 ABI tags exactly (DnsABI.Layout). package com.hyperpolymath.proven /** - * Kotlin bindings for the proven DNS server protocol. + * Kotlin bindings for the bounded proven DNS message-builder FFI. * - * Lifecycle: Idle -> QueryReceived -> Lookup -> ResponseBuilding -> Sent. + * Bounded message builder, not a general resolver. DNSSEC key loading, signing, + * and validation are unavailable; exposed DNSSEC transitions are model tags only. * * @author Jonathan D.A. Jewell */ @@ -82,9 +84,10 @@ public class ProvenDns private constructor(private val slot: Int) : AutoCloseabl public val queryRtype: Int get() = dns_query_rtype(slot) public val queryClass: Int get() = dns_query_class(slot) - /** Parse a DNS query from raw bytes. Transitions Idle -> QueryReceived. */ + /** Parse only the exact 17-byte standard root-question query subset. */ public fun parseQuery(data: ByteArray): Result = ProvenError.runCatching { - ProvenError.checkStatus(dns_parse_query(slot, data, data.size)) + require(data.size == 17) { "DNS query must be exactly 17 bytes" } + ProvenError.checkStatus(dns_parse_query(slot, data, 17)) } /** Begin DNS lookup. Transitions QueryReceived -> Lookup. */ @@ -99,16 +102,19 @@ public class ProvenDns private constructor(private val slot: Int) : AutoCloseabl /** Add a resource record to the answer section. */ public fun addAnswer(rtype: Int, rclass: Int, ttl: Int, rdata: ByteArray): Result = ProvenError.runCatching { + require(rdata.size <= 256) { "DNS RDATA must be at most 256 bytes" } ProvenError.checkStatus(dns_add_answer(slot, rtype, rclass, ttl, rdata, rdata.size)) } /** Add a resource record to the authority section. */ public fun addAuthority(rtype: Int, rclass: Int, ttl: Int, rdata: ByteArray): Result = ProvenError.runCatching { + require(rdata.size <= 256) { "DNS RDATA must be at most 256 bytes" } ProvenError.checkStatus(dns_add_authority(slot, rtype, rclass, ttl, rdata, rdata.size)) } /** Add a resource record to the additional section. */ public fun addAdditional(rtype: Int, rclass: Int, ttl: Int, rdata: ByteArray): Result = ProvenError.runCatching { + require(rdata.size <= 256) { "DNS RDATA must be at most 256 bytes" } ProvenError.checkStatus(dns_add_additional(slot, rtype, rclass, ttl, rdata, rdata.size)) } @@ -117,7 +123,7 @@ public class ProvenDns private constructor(private val slot: Int) : AutoCloseabl ProvenError.checkStatus(dns_set_rcode(slot, rcodeTag)) } - /** Build the DNS response message. Transitions ResponseBuilding -> Sent. */ + /** Build a root-question response; output is capped at 512 bytes. */ public fun buildResponse(): Result = ProvenError.runCatching { val buf = ByteArray(65536) val outLen = IntArray(1) @@ -125,22 +131,22 @@ public class ProvenDns private constructor(private val slot: Int) : AutoCloseabl buf.copyOf(outLen[0]) } - /** Enable DNSSEC. Transitions Disabled -> Enabled. */ + /** Enable mode only; response construction then rejects because no signer exists. */ public fun enableDnssec(): Result = ProvenError.runCatching { ProvenError.checkStatus(dns_enable_dnssec(slot)) } - /** Load a DNSSEC signing key. Transitions Enabled -> KeyLoaded. */ + /** Always fails closed: the ABI accepts no private-key material. */ public fun loadDnssecKey(algorithm: DnssecAlgorithm): Result = ProvenError.runCatching { ProvenError.checkStatus(dns_load_dnssec_key(slot, algorithm.tag)) } - /** Sign the response (DNSSEC). Transitions KeyLoaded -> Validated. */ + /** Always fails closed because no DNSSEC signing backend exists. */ public fun signResponse(): Result = ProvenError.runCatching { ProvenError.checkStatus(dns_sign_response(slot)) } - /** Check DNSSEC validation result. */ + /** Always false because no DNSSEC validator exists. */ public val isDnssecValid: Boolean get() = dns_validate_dnssec(slot) == 0 public companion object { diff --git a/bindings/lua/proven/dns.lua b/bindings/lua/proven/dns.lua index 9d066963..3bb70328 100644 --- a/bindings/lua/proven/dns.lua +++ b/bindings/lua/proven/dns.lua @@ -2,13 +2,15 @@ -- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -- --- @module proven.dns ---- DNS protocol bindings for proven-servers. +--- Bindings for the bounded proven-dns message-builder FFI. --- ---- Mirrors the Idris2 `DNS` module and `DNS.RecordType`. Constants are ---- derived from RFC 1035 and RFC 6891. Record type discriminants match ---- the standard IANA DNS type codes. +--- The FFI accepts only exact 17-byte standard queries with one root-name +--- question, recognized QTYPE/QCLASS, no other sections, and RD as its only +--- supported flag. Responses are capped at 512 bytes. This is not a general +--- resolver; DNSSEC key loading, signing, and validation fail closed. --- ---- @see protocols/proven-dns/src/ for the Idris2 definitions. +--- The RFC constants below describe DNS generally; they do not imply that +--- EDNS, arbitrary QNAMEs, TCP transport, or a network server are implemented. local ffi_mod = require("proven.ffi") local err_mod = require("proven.error") @@ -16,145 +18,245 @@ local err_mod = require("proven.error") local M = {} --------------------------------------------------------------------------- --- DNS Constants (DNS module) +-- DNS constants and ABI tags --------------------------------------------------------------------------- ---- Standard DNS port (RFC 1035). +--- Standard DNS port (RFC 1035); the FFI does not open sockets. M.DNS_PORT = 53 ---- Maximum UDP message size without EDNS (RFC 1035 Section 4.2.1). +--- Standard protocol size constants (RFC 1035/RFC 6891); not FFI capabilities. M.MAX_UDP_SIZE = 512 - ---- Maximum TCP message size (RFC 1035 Section 4.2.2). M.MAX_TCP_SIZE = 65535 - ---- Maximum label length in bytes (RFC 1035 Section 2.3.4). M.MAX_LABEL_LENGTH = 63 - ---- Maximum total domain name length including dots (RFC 1035). M.MAX_NAME_LENGTH = 253 - ---- EDNS(0) default UDP payload size (RFC 6891). M.EDNS_UDP_SIZE = 4096 ---------------------------------------------------------------------------- --- Record Type (DNS.RecordType, IANA type codes) ---------------------------------------------------------------------------- +--- Current FFI parser contract. +M.FFI_QUERY_LENGTH = 17 +M.FFI_MAX_RESPONSE_SIZE = 512 ---- DNS resource record types. ---- Discriminant values are IANA DNS type codes. ---- @table RecordType +--- IANA DNS wire type codes for the model's supported record-type subset. M.RecordType = { - A = 1, -- IPv4 address (RFC 1035) - AAAA = 28, -- IPv6 address (RFC 3596) - CNAME = 5, -- Canonical name (RFC 1035) - MX = 15, -- Mail exchange (RFC 1035) - NS = 2, -- Name server (RFC 1035) - TXT = 16, -- Text record (RFC 1035) - SOA = 6, -- Start of authority (RFC 1035) - SRV = 33, -- Service locator (RFC 2782) - PTR = 12, -- Pointer record (RFC 1035) + A = 1, AAAA = 28, CNAME = 5, MX = 15, NS = 2, + TXT = 16, SOA = 6, SRV = 33, PTR = 12, } ---- Reverse lookup: type code -> name string. -M.RecordTypeName = { - [1] = "A", [28] = "AAAA", [5] = "CNAME", [15] = "MX", - [2] = "NS", [16] = "TXT", [6] = "SOA", [33] = "SRV", - [12] = "PTR", +--- Record-type ABI tags accepted by dns_add_* (not IANA wire codes). +M.RecordTypeTag = { + A = 0, AAAA = 1, CNAME = 2, MX = 3, NS = 4, + PTR = 5, SOA = 6, SRV = 7, TXT = 8, } ---------------------------------------------------------------------------- --- Response Code (DNS RCODE, RFC 1035 Section 4.1.1) ---------------------------------------------------------------------------- +--- Query-class ABI tags accepted by dns_add_*. +M.RecordClassTag = { IN = 0, CH = 1, HS = 2, ANY = 3 } + +--- Corresponding IANA wire class codes. +M.RecordClass = { IN = 1, CH = 3, HS = 4, ANY = 255 } ---- DNS response codes. ---- @table ResponseCode +--- DNS response-code ABI tags. M.ResponseCode = { - NOERROR = 0, - FORMERR = 1, - SERVFAIL = 2, - NXDOMAIN = 3, - NOTIMP = 4, - REFUSED = 5, + NOERROR = 0, FORMERR = 1, SERVFAIL = 2, + NXDOMAIN = 3, NOTIMP = 4, REFUSED = 5, } +M.DnsState = { + IDLE = 0, QUERY_RECEIVED = 1, LOOKUP = 2, + RESPONSE_BUILDING = 3, SENT = 4, +} + +--- DNSSEC states are ABI/model tags only; crypto operations are unavailable. +M.DnssecState = { DISABLED = 0, ENABLED = 1, KEY_LOADED = 2, VALIDATED = 3 } + +local TAG_TO_WIRE = { [0] = 1, [1] = 28, [2] = 5, [3] = 15, [4] = 2, + [5] = 12, [6] = 6, [7] = 33, [8] = 16 } + --------------------------------------------------------------------------- --- Context (OOP wrapper) +-- Context --------------------------------------------------------------------------- ---- DNS context wrapping a slot in the Zig FFI pool. +--- Context wrapping a slot in the bounded Zig FFI pool. --- @type Context local Context = {} Context.__index = Context ---- Create a new DNS context. ---- @return Context A new context, or raises on pool exhaustion. +local function status_result(raw, context) + local ok, err = err_mod.from_status(raw) + if not ok then return nil, err end + return true, nil +end + +--- Create a new DNS message-builder context. +--- @return Context|nil context, or nil and an error key. function Context.new() local lib = ffi_mod.get_lib() - local slot = lib.dns_create_context() - local s, e = err_mod.from_slot(slot) - if not s then err_mod.raise("dns.Context.new", e) end - return setmetatable({ _slot = s, _destroyed = false }, Context) + local slot, err = err_mod.from_slot(lib.dns_create_context()) + if not slot then err_mod.raise("dns.Context.new", err) end + return setmetatable({ _slot = slot, _destroyed = false }, Context) end ---- Destroy the context, releasing the slot. +--- Destroy the context, releasing its slot. function Context:destroy() if not self._destroyed then - local lib = ffi_mod.get_lib() - lib.dns_destroy_context(self._slot) + ffi_mod.get_lib().dns_destroy_context(self._slot) self._destroyed = true end end ---- Parse a DNS query from raw bytes. ---- @param data string Raw DNS query packet. ---- @return boolean|nil true on success. ---- @return string|nil Error key on failure. +--- Get the current lifecycle-state ABI tag. +function Context:get_state() + return ffi_mod.get_lib().dns_state(self._slot) +end + +--- Get the current DNSSEC ABI/model-state tag. +function Context:get_dnssec_state() + return ffi_mod.get_lib().dns_dnssec_state(self._slot) +end + +--- Get the response-code ABI tag. +function Context:get_response_code() + return ffi_mod.get_lib().dns_rcode(self._slot) +end + +--- Get the parsed query's record-type ABI tag (255 means unset). +function Context:get_query_type() + return ffi_mod.get_lib().dns_query_rtype(self._slot) +end + +--- Get the parsed query's record-class ABI tag (255 means unset). +function Context:get_query_class() + return ffi_mod.get_lib().dns_query_class(self._slot) +end + +--- Convert a query-type ABI tag to its IANA wire type, or nil if unsupported. +function Context:get_query_type_wire() + return TAG_TO_WIRE[self:get_query_type()] +end + +--- Get section record counts as an answer/authority/additional triple. +function Context:get_record_counts() + local lib = ffi_mod.get_lib() + return lib.dns_answer_count(self._slot), + lib.dns_authority_count(self._slot), + lib.dns_additional_count(self._slot) +end + +--- Parse only an exact 17-byte standard root-question query. +--- @param data string raw DNS query bytes +--- @return boolean|nil true on success, nil plus an error key on failure function Context:parse_query(data) + if type(data) ~= "string" or #data ~= M.FFI_QUERY_LENGTH then + return nil, err_mod.ProvenError.INVALID_PARAMETER + end local lib = ffi_mod.get_lib() local ffi = ffi_mod.ffi local buf = ffi.cast("const uint8_t *", data) - return err_mod.from_status(lib.dns_parse_query(self._slot, buf, #data)) + return status_result(lib.dns_parse_query(self._slot, buf, #data), "dns.parse_query") end ---- Get the parsed query record type tag. ---- @return number Record type code, see `M.RecordType`. -function Context:get_query_type() - local lib = ffi_mod.get_lib() - return lib.dns_get_query_type(self._slot) +--- Transition QueryReceived -> Lookup. +function Context:begin_lookup() + return status_result(ffi_mod.get_lib().dns_begin_lookup(self._slot), "dns.begin_lookup") end ---- Set the response code. ---- @param rcode number Response code, see `M.ResponseCode`. ---- @return boolean|nil true on success. ---- @return string|nil Error key on failure. -function Context:set_response_code(rcode) - local lib = ffi_mod.get_lib() - return err_mod.from_status(lib.dns_set_response_code(self._slot, rcode)) +--- Transition Lookup -> ResponseBuilding. +function Context:begin_response() + return status_result(ffi_mod.get_lib().dns_begin_response(self._slot), "dns.begin_response") end ---- Add a resource record to the response. ---- @param rtype number Record type code. ---- @param rdata string Record data bytes. ---- @return boolean|nil true on success. ---- @return string|nil Error key on failure. -function Context:add_record(rtype, rdata) +--- Set the response-code ABI tag. +function Context:set_response_code(rcode_tag) + if type(rcode_tag) ~= "number" or rcode_tag < 0 or rcode_tag > 10 or rcode_tag % 1 ~= 0 then + return nil, err_mod.ProvenError.INVALID_PARAMETER + end + return status_result(ffi_mod.get_lib().dns_set_rcode(self._slot, rcode_tag), "dns.set_response_code") +end + +local function add_record(ctx, function_name, rtype_tag, rclass_tag, ttl, rdata) + if type(rdata) ~= "string" or #rdata > 256 then + return nil, err_mod.ProvenError.CAPACITY_EXCEEDED + end + if type(rtype_tag) ~= "number" or rtype_tag < 0 or rtype_tag > 14 or rtype_tag % 1 ~= 0 or + type(rclass_tag) ~= "number" or rclass_tag < 0 or rclass_tag > 3 or rclass_tag % 1 ~= 0 or + type(ttl) ~= "number" or ttl < 0 or ttl > 4294967295 or ttl % 1 ~= 0 then + return nil, err_mod.ProvenError.INVALID_PARAMETER + end local lib = ffi_mod.get_lib() local ffi = ffi_mod.ffi local buf = ffi.cast("const uint8_t *", rdata) - return err_mod.from_status(lib.dns_add_record(self._slot, rtype, buf, #rdata)) + return status_result(lib[function_name](ctx._slot, rtype_tag, rclass_tag, ttl, buf, #rdata), + "dns." .. function_name) end ---- Send the constructed DNS response. ---- @return boolean|nil true on success. ---- @return string|nil Error key on failure. -function Context:send_response() +--- Add an answer RR. Type/class arguments are ABI tags; RDATA is at most 256 bytes. +function Context:add_answer(rtype_tag, rclass_tag, ttl, rdata) + return add_record(self, "dns_add_answer", rtype_tag, rclass_tag, ttl, rdata) +end + +--- Add an authority RR. Type/class arguments are ABI tags; RDATA is at most 256 bytes. +function Context:add_authority(rtype_tag, rclass_tag, ttl, rdata) + return add_record(self, "dns_add_authority", rtype_tag, rclass_tag, ttl, rdata) +end + +--- Add an additional RR. Type/class arguments are ABI tags; RDATA is at most 256 bytes. +function Context:add_additional(rtype_tag, rclass_tag, ttl, rdata) + return add_record(self, "dns_add_additional", rtype_tag, rclass_tag, ttl, rdata) +end + +--- Build the bounded DNS response bytes (at most 512); no network I/O occurs. +--- @return string|nil response or nil plus an error key +function Context:build_response() + local ffi = ffi_mod.ffi local lib = ffi_mod.get_lib() - return err_mod.from_status(lib.dns_send_response(self._slot)) + local out = ffi.new("uint8_t[512]") + local out_len = ffi.new("uint16_t[1]") + local ok, err = status_result(lib.dns_build_response(self._slot, out, out_len), "dns.build_response") + if not ok then return nil, err end + return ffi.string(out, tonumber(out_len[0])) end -Context.__gc = Context.destroy +--- Deprecated alias for build_response; this binding does not send packets. +function Context:send_response() + return self:build_response() +end +--- Enable DNSSEC mode only; subsequent response construction rejects without a signer. +function Context:enable_dnssec() + return status_result(ffi_mod.get_lib().dns_enable_dnssec(self._slot), "dns.enable_dnssec") +end + +--- DNSSEC key loading always fails closed because the ABI has no key bytes. +function Context:load_dnssec_key(algo_tag) + if type(algo_tag) ~= "number" or algo_tag < 0 or algo_tag > 4 or algo_tag % 1 ~= 0 then + return nil, err_mod.ProvenError.INVALID_PARAMETER + end + return status_result(ffi_mod.get_lib().dns_load_dnssec_key(self._slot, algo_tag), "dns.load_dnssec_key") +end + +--- DNSSEC signing always fails closed because no signing backend exists. +function Context:sign_response() + return status_result(ffi_mod.get_lib().dns_sign_response(self._slot), "dns.sign_response") +end + +--- DNSSEC validation always fails closed because no validator exists. +function Context:validate_dnssec() + return ffi_mod.get_lib().dns_validate_dnssec(self._slot) == 0 +end + +Context.__gc = Context.destroy M.Context = Context +--- Check a query lifecycle transition. This is a model check, not an operation. +function M.can_transition(from_tag, to_tag) + return ffi_mod.get_lib().dns_can_transition(from_tag, to_tag) == 1 +end + +--- Check an abstract DNSSEC transition; this does not imply crypto availability. +function M.can_dnssec_transition(from_tag, to_tag) + return ffi_mod.get_lib().dns_can_dnssec_transition(from_tag, to_tag) == 1 +end + +function M.abi_version() + return ffi_mod.get_lib().dns_abi_version() +end + return M diff --git a/bindings/lua/proven/ffi.lua b/bindings/lua/proven/ffi.lua index 8cd048be..d7459192 100644 --- a/bindings/lua/proven/ffi.lua +++ b/bindings/lua/proven/ffi.lua @@ -100,15 +100,35 @@ ffi.cdef[[ uint8_t http_reset_context(int slot); uint8_t http_can_transition(uint8_t from_phase, uint8_t to_phase); - /* ---- DNS (proven-dns) ---- */ + /* ---- DNS (proven-dns bounded root-question message builder) ---- */ + uint32_t dns_abi_version(void); int dns_create_context(void); void dns_destroy_context(int slot); - uint8_t dns_parse_query(int slot, const uint8_t *data, uint32_t len); - uint8_t dns_get_query_type(int slot); - uint32_t dns_get_query_name(int slot, uint8_t *buf, uint32_t len); - uint8_t dns_set_response_code(int slot, uint8_t rcode); - uint8_t dns_add_record(int slot, uint8_t rtype, const uint8_t *rdata, uint32_t rlen); - uint8_t dns_send_response(int slot); + uint8_t dns_state(int slot); + uint8_t dns_dnssec_state(int slot); + uint8_t dns_rcode(int slot); + uint16_t dns_answer_count(int slot); + uint16_t dns_authority_count(int slot); + uint16_t dns_additional_count(int slot); + uint8_t dns_query_rtype(int slot); + uint8_t dns_query_class(int slot); + uint8_t dns_parse_query(int slot, const uint8_t *data, uint16_t len); + uint8_t dns_begin_lookup(int slot); + uint8_t dns_begin_response(int slot); + uint8_t dns_add_answer(int slot, uint8_t rtype, uint8_t rclass, + uint32_t ttl, const uint8_t *rdata, uint16_t rdlen); + uint8_t dns_add_authority(int slot, uint8_t rtype, uint8_t rclass, + uint32_t ttl, const uint8_t *rdata, uint16_t rdlen); + uint8_t dns_add_additional(int slot, uint8_t rtype, uint8_t rclass, + uint32_t ttl, const uint8_t *rdata, uint16_t rdlen); + uint8_t dns_set_rcode(int slot, uint8_t rcode); + uint8_t dns_build_response(int slot, uint8_t *out, uint16_t *out_len); + uint8_t dns_enable_dnssec(int slot); + uint8_t dns_load_dnssec_key(int slot, uint8_t algo); + uint8_t dns_sign_response(int slot); + uint8_t dns_validate_dnssec(int slot); + uint8_t dns_can_transition(uint8_t from, uint8_t to); + uint8_t dns_can_dnssec_transition(uint8_t from, uint8_t to); /* ---- SMTP (proven-smtp) ---- */ int smtp_create_context(void); diff --git a/bindings/ocaml/README.adoc b/bindings/ocaml/README.adoc new file mode 100644 index 00000000..81cd5fbb --- /dev/null +++ b/bindings/ocaml/README.adoc @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) += OCaml binding status + +== Native FFI is unavailable + +The OCaml modules contain ABI-tag and lifecycle-model wrappers, but native +operations are intentionally disabled. Earlier declarations used OCaml +`external` primitives to name raw Zig C exports directly. Those calling +conventions are incompatible: OCaml primitives receive OCaml `value`s, while +the Zig exports accept ordinary C ABI values. Calling the raw exports through +those declarations could corrupt memory or crash. + +All former raw externals now raise a clear `Failure` through +`Proven_unavailable.raise_unavailable`. The Dune library no longer links the +protocol C libraries. Do not describe or use this as a functioning native +binding until OCaml-compatible C stubs are implemented and tested. + +== Required follow-up + +* Add C stubs using the OCaml runtime ABI, with explicit conversion and + validation for each native function. +* Declare those stubs through Dune `foreign_stubs` and link only the required + protocol libraries. +* Compile and test every binding with the supported OCaml and Dune versions; + those tools were unavailable during the current audit. +* Keep the fail-closed OCaml wrappers until the native bridge is verified. diff --git a/bindings/ocaml/dune-project b/bindings/ocaml/dune-project index 78fbc717..5145f845 100644 --- a/bindings/ocaml/dune-project +++ b/bindings/ocaml/dune-project @@ -1,9 +1,8 @@ ; SPDX-License-Identifier: MPL-2.0 ; Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) ; -; Dune project file for proven-servers OCaml bindings. -; Provides type-safe wrappers around the Zig FFI C exports for all -; 10 core proven-servers protocols. +; Dune project file for the current OCaml model/tag scaffolds. +; Native operations fail closed until OCaml-compatible C stubs are implemented. (lang dune 3.0) @@ -18,11 +17,11 @@ (package (name proven_servers) - (synopsis "OCaml bindings for proven-servers formally verified protocol ABI") + (synopsis "proven-servers OCaml model scaffolds (native FFI unavailable)") (description - "Type-safe OCaml bindings for the proven-servers project. Wraps the \ - Zig FFI C exports for 10 core protocols (HTTP, DNS, SMTP, FTP, SSH \ - Bastion, MQTT, gRPC, GraphQL, TLS, Firewall) using external \ - declarations with result-type wrappers.") + "OCaml tag and model scaffolds for proven-servers. Native operations are \ + intentionally unavailable: direct OCaml external declarations to raw Zig \ + C symbols used the wrong calling convention. Implement OCaml-compatible C \ + stubs before advertising or enabling native FFI calls.") (depends (ocaml (>= 4.14)))) diff --git a/bindings/ocaml/lib/dune b/bindings/ocaml/lib/dune index 98dafa38..27757302 100644 --- a/bindings/ocaml/lib/dune +++ b/bindings/ocaml/lib/dune @@ -4,34 +4,9 @@ (library (name proven_servers) (public_name proven_servers) - (c_library_flags - (-lproven_agentic -lproven_airgap -lproven_amqp -lproven_apiserver - -lproven_appserver -lproven_authserver -lproven_backup -lproven_bfd - -lproven_bgp -lproven_ca -lproven_cache -lproven_caldav - -lproven_carddav -lproven_chat -lproven_coap -lproven_configmgmt - -lproven_container -lproven_ctlog -lproven_dbserver -lproven_dds - -lproven_deception -lproven_dhcp -lproven_diode -lproven_dns - -lproven_doh -lproven_doq -lproven_dot -lproven_federation - -lproven_fileserver -lproven_firewall -lproven_ftp -lproven_gameserver - -lproven_git -lproven_graphdb -lproven_graphql -lproven_grpc - -lproven_hardened -lproven_honeypot -lproven_http -lproven_httpd - -lproven_ids -lproven_imap -lproven_irc -lproven_kerberos - -lproven_kms -lproven_ldap -lproven_ldp -lproven_loadbalancer - -lproven_logcollector -lproven_lpd -lproven_mcp -lproven_mdns - -lproven_media -lproven_metrics -lproven_modbus -lproven_monitor - -lproven_mqtt -lproven_nesy -lproven_netconf -lproven_neurosym - -lproven_nfs -lproven_ntp -lproven_nts -lproven_objectstore - -lproven_ocsp -lproven_odns -lproven_opcua -lproven_ospf - -lproven_pop3 -lproven_pqc -lproven_proxy -lproven_ptp - -lproven_radius -lproven_rtsp -lproven_sandbox -lproven_sdn - -lproven_semweb -lproven_siem -lproven_smb -lproven_smtp - -lproven_snmp -lproven_socks -lproven_sparql -lproven_ssh - -lproven_ssh_bastion -lproven_stun -lproven_syslog -lproven_tacacs - -lproven_telnet -lproven_tftp -lproven_tls -lproven_triplestore - -lproven_virt -lproven_voip -lproven_vpn -lproven_wasm - -lproven_webdav -lproven_websocket -lproven_xmpp -lproven_zerotrust)) + (modules - proven_servers proven_error + proven_servers proven_error proven_unavailable proven_agentic proven_airgap proven_amqp proven_apiserver proven_appserver proven_authserver proven_backup proven_bfd proven_bgp proven_ca proven_cache proven_caldav diff --git a/bindings/ocaml/lib/proven_agentic.ml b/bindings/ocaml/lib/proven_agentic.ml index 5d0360df..591a1396 100644 --- a/bindings/ocaml/lib/proven_agentic.ml +++ b/bindings/ocaml/lib/proven_agentic.ml @@ -98,13 +98,20 @@ let safety_check_of_tag = function | 3 -> Some Timeout | 4 -> Some Sandboxed | 5 -> Some HumanRequired | _ -> None -(* --- C FFI declarations --- *) - -external c_agentic_abi_version : unit -> int = "agentic_abi_version" -external c_agentic_create_context : unit -> int = "agentic_create_context" -external c_agentic_destroy_context : int -> unit = "agentic_destroy_context" -external c_agentic_state : int -> int = "agentic_state" -external c_agentic_can_transition : int -> int -> int = "agentic_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_agentic_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_agentic_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_agentic_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_agentic_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_agentic_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_airgap.ml b/bindings/ocaml/lib/proven_airgap.ml index 8b537d30..eb9872a3 100644 --- a/bindings/ocaml/lib/proven_airgap.ml +++ b/bindings/ocaml/lib/proven_airgap.ml @@ -80,13 +80,20 @@ let validation_check_of_tag = function | 2 -> Some FormatCheck | 3 -> Some ContentInspection | 4 -> Some MalwareScan | _ -> None -(* --- C FFI declarations --- *) - -external c_airgap_abi_version : unit -> int = "airgap_abi_version" -external c_airgap_create_context : unit -> int = "airgap_create_context" -external c_airgap_destroy_context : int -> unit = "airgap_destroy_context" -external c_airgap_state : int -> int = "airgap_state" -external c_airgap_can_transition : int -> int -> int = "airgap_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_airgap_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_airgap_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_airgap_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_airgap_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_airgap_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_amqp.ml b/bindings/ocaml/lib/proven_amqp.ml index c0212508..342dab92 100644 --- a/bindings/ocaml/lib/proven_amqp.ml +++ b/bindings/ocaml/lib/proven_amqp.ml @@ -121,13 +121,20 @@ let broker_state_of_tag = function | 3 -> Some Consuming | 4 -> Some Publishing | 5 -> Some Disconnecting | _ -> None -(* --- C FFI declarations --- *) - -external c_amqp_abi_version : unit -> int = "amqp_abi_version" -external c_amqp_create_context : unit -> int = "amqp_create_context" -external c_amqp_destroy_context : int -> unit = "amqp_destroy_context" -external c_amqp_state : int -> int = "amqp_state" -external c_amqp_can_transition : int -> int -> int = "amqp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_amqp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_amqp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_amqp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_amqp_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_amqp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_apiserver.ml b/bindings/ocaml/lib/proven_apiserver.ml index 6e0a713f..d43cc3b0 100644 --- a/bindings/ocaml/lib/proven_apiserver.ml +++ b/bindings/ocaml/lib/proven_apiserver.ml @@ -85,13 +85,20 @@ let gateway_error_of_tag = function | 3 -> Some BadRequest | 4 -> Some ServiceUnavailable | 5 -> Some CircuitOpen | _ -> None -(* --- C FFI declarations --- *) - -external c_apiserver_abi_version : unit -> int = "apiserver_abi_version" -external c_apiserver_create_context : unit -> int = "apiserver_create_context" -external c_apiserver_destroy_context : int -> unit = "apiserver_destroy_context" -external c_apiserver_state : int -> int = "apiserver_state" -external c_apiserver_can_transition : int -> int -> int = "apiserver_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_apiserver_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_apiserver_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_apiserver_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_apiserver_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_apiserver_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_appserver.ml b/bindings/ocaml/lib/proven_appserver.ml index 1a9b8f67..780b0745 100644 --- a/bindings/ocaml/lib/proven_appserver.ml +++ b/bindings/ocaml/lib/proven_appserver.ml @@ -81,13 +81,20 @@ let error_category_of_tag = function | 0 -> Some ClientError | 1 -> Some ServerError | 2 -> Some Timeout | 3 -> Some CircuitOpen | 4 -> Some RateLimited | _ -> None -(* --- C FFI declarations --- *) - -external c_appserver_abi_version : unit -> int = "appserver_abi_version" -external c_appserver_create_context : unit -> int = "appserver_create_context" -external c_appserver_destroy_context : int -> unit = "appserver_destroy_context" -external c_appserver_state : int -> int = "appserver_state" -external c_appserver_can_transition : int -> int -> int = "appserver_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_appserver_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_appserver_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_appserver_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_appserver_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_appserver_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_authserver.ml b/bindings/ocaml/lib/proven_authserver.ml index 049df481..654120bf 100644 --- a/bindings/ocaml/lib/proven_authserver.ml +++ b/bindings/ocaml/lib/proven_authserver.ml @@ -87,13 +87,20 @@ let session_state_of_tag = function | 0 -> Some Active | 1 -> Some Expired | 2 -> Some Revoked | 3 -> Some Locked | _ -> None -(* --- C FFI declarations --- *) - -external c_authserver_abi_version : unit -> int = "authserver_abi_version" -external c_authserver_create_context : unit -> int = "authserver_create_context" -external c_authserver_destroy_context : int -> unit = "authserver_destroy_context" -external c_authserver_state : int -> int = "authserver_state" -external c_authserver_can_transition : int -> int -> int = "authserver_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_authserver_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_authserver_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_authserver_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_authserver_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_authserver_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_backup.ml b/bindings/ocaml/lib/proven_backup.ml index 5b86c3f1..8cf966ac 100644 --- a/bindings/ocaml/lib/proven_backup.ml +++ b/bindings/ocaml/lib/proven_backup.ml @@ -99,13 +99,20 @@ let retention_policy_of_tag = function | 0 -> Some KeepAll | 1 -> Some KeepLast | 2 -> Some KeepDaily | 3 -> Some KeepWeekly | 4 -> Some KeepMonthly | _ -> None -(* --- C FFI declarations --- *) - -external c_backup_abi_version : unit -> int = "backup_abi_version" -external c_backup_create_context : unit -> int = "backup_create_context" -external c_backup_destroy_context : int -> unit = "backup_destroy_context" -external c_backup_state : int -> int = "backup_state" -external c_backup_can_transition : int -> int -> int = "backup_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_backup_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_backup_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_backup_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_backup_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_backup_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_bfd.ml b/bindings/ocaml/lib/proven_bfd.ml index 70b0b5a1..faeaa1c8 100644 --- a/bindings/ocaml/lib/proven_bfd.ml +++ b/bindings/ocaml/lib/proven_bfd.ml @@ -74,13 +74,20 @@ let session_state_of_tag = function | 0 -> Some Idle | 1 -> Some SsDown | 2 -> Some Negotiating | 3 -> Some Established | 4 -> Some Teardown | _ -> None -(* --- C FFI declarations --- *) - -external c_bfd_abi_version : unit -> int = "bfd_abi_version" -external c_bfd_create_context : unit -> int = "bfd_create_context" -external c_bfd_destroy_context : int -> unit = "bfd_destroy_context" -external c_bfd_state : int -> int = "bfd_state" -external c_bfd_can_transition : int -> int -> int = "bfd_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_bfd_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_bfd_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_bfd_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_bfd_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_bfd_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_bgp.ml b/bindings/ocaml/lib/proven_bgp.ml index d81b0d61..6912e183 100644 --- a/bindings/ocaml/lib/proven_bgp.ml +++ b/bindings/ocaml/lib/proven_bgp.ml @@ -142,13 +142,20 @@ let path_attr_type_of_tag = function | 3 -> Some Med | 4 -> Some LocalPref | 5 -> Some AtomicAggr | 6 -> Some Aggregator | 7 -> Some Unknown | _ -> None -(* --- C FFI declarations --- *) - -external c_bgp_abi_version : unit -> int = "bgp_abi_version" -external c_bgp_create_context : unit -> int = "bgp_create_context" -external c_bgp_destroy_context : int -> unit = "bgp_destroy_context" -external c_bgp_state : int -> int = "bgp_state" -external c_bgp_can_transition : int -> int -> int = "bgp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_bgp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_bgp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_bgp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_bgp_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_bgp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ca.ml b/bindings/ocaml/lib/proven_ca.ml index 9bd14496..f0138d60 100644 --- a/bindings/ocaml/lib/proven_ca.ml +++ b/bindings/ocaml/lib/proven_ca.ml @@ -171,13 +171,20 @@ let key_usage_bit_of_tag = function | 4 -> Some KeyAgreement | 5 -> Some KeyCertSign | 6 -> Some CrlSign | 7 -> Some EncipherOnly | 8 -> Some DecipherOnly | _ -> None -(* --- C FFI declarations --- *) - -external c_ca_abi_version : unit -> int = "ca_abi_version" -external c_ca_create_context : unit -> int = "ca_create_context" -external c_ca_destroy_context : int -> unit = "ca_destroy_context" -external c_ca_state : int -> int = "ca_state" -external c_ca_can_transition : int -> int -> int = "ca_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ca_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ca_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ca_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ca_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ca_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_cache.ml b/bindings/ocaml/lib/proven_cache.ml index 020bb370..bddc7570 100644 --- a/bindings/ocaml/lib/proven_cache.ml +++ b/bindings/ocaml/lib/proven_cache.ml @@ -96,13 +96,20 @@ let replication_mode_of_tag = function | 0 -> Some ReplNone | 1 -> Some Primary | 2 -> Some Replica | 3 -> Some Sentinel | _ -> None -(* --- C FFI declarations --- *) - -external c_cache_abi_version : unit -> int = "cache_abi_version" -external c_cache_create_context : unit -> int = "cache_create_context" -external c_cache_destroy_context : int -> unit = "cache_destroy_context" -external c_cache_state : int -> int = "cache_state" -external c_cache_can_transition : int -> int -> int = "cache_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_cache_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_cache_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_cache_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_cache_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_cache_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_caldav.ml b/bindings/ocaml/lib/proven_caldav.ml index 1f625665..77ff0207 100644 --- a/bindings/ocaml/lib/proven_caldav.ml +++ b/bindings/ocaml/lib/proven_caldav.ml @@ -89,13 +89,20 @@ let server_state_of_tag = function | 0 -> Some Idle | 1 -> Some Bound | 2 -> Some Serving | 3 -> Some Scheduling | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_caldav_abi_version : unit -> int = "caldav_abi_version" -external c_caldav_create_context : unit -> int = "caldav_create_context" -external c_caldav_destroy_context : int -> unit = "caldav_destroy_context" -external c_caldav_state : int -> int = "caldav_state" -external c_caldav_can_transition : int -> int -> int = "caldav_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_caldav_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_caldav_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_caldav_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_caldav_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_caldav_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_carddav.ml b/bindings/ocaml/lib/proven_carddav.ml index b3df238c..56067739 100644 --- a/bindings/ocaml/lib/proven_carddav.ml +++ b/bindings/ocaml/lib/proven_carddav.ml @@ -89,13 +89,20 @@ let server_state_of_tag = function | 0 -> Some Idle | 1 -> Some Bound | 2 -> Some Serving | 3 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_carddav_abi_version : unit -> int = "carddav_abi_version" -external c_carddav_create_context : unit -> int = "carddav_create_context" -external c_carddav_destroy_context : int -> unit = "carddav_destroy_context" -external c_carddav_state : int -> int = "carddav_state" -external c_carddav_can_transition : int -> int -> int = "carddav_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_carddav_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_carddav_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_carddav_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_carddav_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_carddav_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_chat.ml b/bindings/ocaml/lib/proven_chat.ml index 33f5da55..a0735837 100644 --- a/bindings/ocaml/lib/proven_chat.ml +++ b/bindings/ocaml/lib/proven_chat.ml @@ -95,13 +95,20 @@ let event_of_tag = function | 2 -> Some MessageRead | 3 -> Some UserJoined | 4 -> Some UserLeft | 5 -> Some Typing | 6 -> Some RoomCreated | _ -> None -(* --- C FFI declarations --- *) - -external c_chat_abi_version : unit -> int = "chat_abi_version" -external c_chat_create_context : unit -> int = "chat_create_context" -external c_chat_destroy_context : int -> unit = "chat_destroy_context" -external c_chat_state : int -> int = "chat_state" -external c_chat_can_transition : int -> int -> int = "chat_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_chat_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_chat_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_chat_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_chat_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_chat_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_coap.ml b/bindings/ocaml/lib/proven_coap.ml index 9904fd6a..497246e8 100644 --- a/bindings/ocaml/lib/proven_coap.ml +++ b/bindings/ocaml/lib/proven_coap.ml @@ -85,13 +85,20 @@ let session_state_of_tag = function | 0 -> Some Idle | 1 -> Some Bound | 2 -> Some Serving | 3 -> Some Observing | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_coap_abi_version : unit -> int = "coap_abi_version" -external c_coap_create_context : unit -> int = "coap_create_context" -external c_coap_destroy_context : int -> unit = "coap_destroy_context" -external c_coap_state : int -> int = "coap_state" -external c_coap_can_transition : int -> int -> int = "coap_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_coap_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_coap_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_coap_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_coap_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_coap_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_configmgmt.ml b/bindings/ocaml/lib/proven_configmgmt.ml index ff116128..e1738ef8 100644 --- a/bindings/ocaml/lib/proven_configmgmt.ml +++ b/bindings/ocaml/lib/proven_configmgmt.ml @@ -87,13 +87,20 @@ let apply_mode_to_tag = function let apply_mode_of_tag = function | 0 -> Some Enforce | 1 -> Some DryRun | 2 -> Some Audit | _ -> None -(* --- C FFI declarations --- *) - -external c_configmgmt_abi_version : unit -> int = "configmgmt_abi_version" -external c_configmgmt_create_context : unit -> int = "configmgmt_create_context" -external c_configmgmt_destroy_context : int -> unit = "configmgmt_destroy_context" -external c_configmgmt_state : int -> int = "configmgmt_state" -external c_configmgmt_can_transition : int -> int -> int = "configmgmt_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_configmgmt_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_configmgmt_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_configmgmt_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_configmgmt_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_configmgmt_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_container.ml b/bindings/ocaml/lib/proven_container.ml index 8610e163..15fd96c2 100644 --- a/bindings/ocaml/lib/proven_container.ml +++ b/bindings/ocaml/lib/proven_container.ml @@ -103,13 +103,20 @@ let health_status_of_tag = function | 0 -> Some Starting | 1 -> Some Healthy | 2 -> Some Unhealthy | 3 -> Some NoCheck | _ -> None -(* --- C FFI declarations --- *) - -external c_container_abi_version : unit -> int = "container_abi_version" -external c_container_create_context : unit -> int = "container_create_context" -external c_container_destroy_context : int -> unit = "container_destroy_context" -external c_container_state : int -> int = "container_state" -external c_container_can_transition : int -> int -> int = "container_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_container_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_container_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_container_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_container_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_container_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ctlog.ml b/bindings/ocaml/lib/proven_ctlog.ml index 0627c433..6df459bc 100644 --- a/bindings/ocaml/lib/proven_ctlog.ml +++ b/bindings/ocaml/lib/proven_ctlog.ml @@ -112,13 +112,20 @@ let server_state_of_tag = function | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_ctlog_abi_version : unit -> int = "ctlog_abi_version" -external c_ctlog_create_context : unit -> int = "ctlog_create_context" -external c_ctlog_destroy_context : int -> unit = "ctlog_destroy_context" -external c_ctlog_state : int -> int = "ctlog_state" -external c_ctlog_can_transition : int -> int -> int = "ctlog_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ctlog_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ctlog_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ctlog_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ctlog_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ctlog_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_dbserver.ml b/bindings/ocaml/lib/proven_dbserver.ml index 3dfd7651..5d1215f6 100644 --- a/bindings/ocaml/lib/proven_dbserver.ml +++ b/bindings/ocaml/lib/proven_dbserver.ml @@ -192,13 +192,20 @@ let session_state_of_tag = function | 5 -> Some Disconnecting | _ -> None -(* --- C FFI declarations --- *) - -external c_dbserver_abi_version : unit -> int = "dbserver_abi_version" -external c_dbserver_create_context : unit -> int = "dbserver_create_context" -external c_dbserver_destroy_context : int -> unit = "dbserver_destroy_context" -external c_dbserver_state : int -> int = "dbserver_state" -external c_dbserver_can_transition : int -> int -> int = "dbserver_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_dbserver_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_dbserver_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_dbserver_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dbserver_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dbserver_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_dds.ml b/bindings/ocaml/lib/proven_dds.ml index 95d1d380..b3c97152 100644 --- a/bindings/ocaml/lib/proven_dds.ml +++ b/bindings/ocaml/lib/proven_dds.ml @@ -109,13 +109,20 @@ let participant_state_of_tag = function | 4 -> Some Leaving | _ -> None -(* --- C FFI declarations --- *) - -external c_dds_abi_version : unit -> int = "dds_abi_version" -external c_dds_create_context : unit -> int = "dds_create_context" -external c_dds_destroy_context : int -> unit = "dds_destroy_context" -external c_dds_state : int -> int = "dds_state" -external c_dds_can_transition : int -> int -> int = "dds_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_dds_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_dds_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_dds_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dds_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dds_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_deception.ml b/bindings/ocaml/lib/proven_deception.ml index c766af03..d2dc00a6 100644 --- a/bindings/ocaml/lib/proven_deception.ml +++ b/bindings/ocaml/lib/proven_deception.ml @@ -138,14 +138,22 @@ let server_state_of_tag = function | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_deception_abi_version : unit -> int = "deception_abi_version" -external c_deception_create_context : unit -> int = "deception_create_context" -external c_deception_destroy_context : int -> unit = "deception_destroy_context" -external c_deception_state : int -> int = "deception_state" -external c_deception_server_state : int -> int = "deception_server_state" -external c_deception_can_transition : int -> int -> int = "deception_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_deception_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_deception_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_deception_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_deception_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_deception_server_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_deception_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_dhcp.ml b/bindings/ocaml/lib/proven_dhcp.ml index 1d7033ad..649f5823 100644 --- a/bindings/ocaml/lib/proven_dhcp.ml +++ b/bindings/ocaml/lib/proven_dhcp.ml @@ -152,14 +152,22 @@ let relay_sub_option_of_tag = function | 1 -> Some RemoteId | _ -> None -(* --- C FFI declarations --- *) - -external c_dhcp_abi_version : unit -> int = "dhcp_abi_version" -external c_dhcp_create_context : unit -> int = "dhcp_create_context" -external c_dhcp_destroy_context : int -> unit = "dhcp_destroy_context" -external c_dhcp_state : int -> int = "dhcp_state" -external c_dhcp_lease_state : int -> int = "dhcp_lease_state" -external c_dhcp_can_transition : int -> int -> int = "dhcp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_dhcp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_dhcp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_dhcp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dhcp_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dhcp_lease_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dhcp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_diode.ml b/bindings/ocaml/lib/proven_diode.ml index 3cdd55ee..d574aafa 100644 --- a/bindings/ocaml/lib/proven_diode.ml +++ b/bindings/ocaml/lib/proven_diode.ml @@ -119,14 +119,22 @@ let gateway_state_of_tag = function | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_diode_abi_version : unit -> int = "diode_abi_version" -external c_diode_create_context : unit -> int = "diode_create_context" -external c_diode_destroy_context : int -> unit = "diode_destroy_context" -external c_diode_state : int -> int = "diode_state" -external c_diode_gateway_state : int -> int = "diode_gateway_state" -external c_diode_can_transition : int -> int -> int = "diode_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_diode_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_diode_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_diode_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_diode_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_diode_gateway_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_diode_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_dns.ml b/bindings/ocaml/lib/proven_dns.ml index ac69dfd4..1d8d62e5 100644 --- a/bindings/ocaml/lib/proven_dns.ml +++ b/bindings/ocaml/lib/proven_dns.ml @@ -1,10 +1,13 @@ (* SPDX-License-Identifier: MPL-2.0 *) (* Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) *) -(** DNS protocol bindings for proven-servers. +(** Partial DNS lifecycle and ABI-tag declarations for the bounded + root-question message-builder; this is not a general resolver and DNSSEC + crypto fails closed. - Wraps the C-ABI functions from - [protocols/proven-dns/ffi/zig/src/dns.zig]. *) + Native operations deliberately raise [Failure] until OCaml-compatible C + stubs are implemented. The previous direct references to raw Zig C symbols + had an incompatible calling convention and were removed. *) (** DNS query lifecycle states matching [DnsState] in dns.zig. *) type dns_state = @@ -17,9 +20,9 @@ type dns_state = (** DNSSEC lifecycle states matching [DnssecState] in dns.zig. *) type dnssec_state = | Disabled (** DNSSEC disabled. *) - | Enabled (** DNSSEC enabled, no key loaded. *) - | Key_loaded (** DNSSEC key loaded. *) - | Validated (** Response validated / signed. *) + | Enabled (** ABI mode bit; response construction then fails closed. *) + | Key_loaded (** Abstract model state; the FFI cannot load a key. *) + | Validated (** Abstract model state; the FFI cannot validate DNSSEC. *) (** DNSSEC signing algorithms matching [DnssecAlgorithm] in dns.zig. *) type dnssec_algorithm = @@ -48,26 +51,46 @@ let algorithm_to_tag = function | Rsa_sha256 -> 0 | Rsa_sha512 -> 1 | Ecdsa_p256_sha256 -> 2 | Ecdsa_p384_sha384 -> 3 | Ed25519 -> 4 -(* --- C FFI declarations --- *) - -external c_dns_abi_version : unit -> int = "dns_abi_version" -external c_dns_create_context : unit -> int = "dns_create_context" -external c_dns_destroy_context : int -> unit = "dns_destroy_context" -external c_dns_state : int -> int = "dns_state" -external c_dns_dnssec_state : int -> int = "dns_dnssec_state" -external c_dns_rcode : int -> int = "dns_rcode" -external c_dns_answer_count : int -> int = "dns_answer_count" -external c_dns_authority_count : int -> int = "dns_authority_count" -external c_dns_additional_count : int -> int = "dns_additional_count" -external c_dns_begin_lookup : int -> int = "dns_begin_lookup" -external c_dns_begin_response : int -> int = "dns_begin_response" -external c_dns_set_rcode : int -> int -> int = "dns_set_rcode" -external c_dns_enable_dnssec : int -> int = "dns_enable_dnssec" -external c_dns_load_dnssec_key : int -> int -> int = "dns_load_dnssec_key" -external c_dns_sign_response : int -> int = "dns_sign_response" -external c_dns_validate_dnssec : int -> int = "dns_validate_dnssec" -external c_dns_can_transition : int -> int -> int = "dns_can_transition" -external c_dns_can_dnssec_transition : int -> int -> int = "dns_can_dnssec_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_dns_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_dns_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_dns_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_dnssec_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_rcode : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_answer_count : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_authority_count : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_additional_count : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_begin_lookup : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_begin_response : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_set_rcode : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_dns_enable_dnssec : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_load_dnssec_key : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_dns_sign_response : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_validate_dnssec : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dns_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_dns_can_dnssec_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) @@ -95,13 +118,17 @@ let begin_response slot = Proven_error.from_status (c_dns_begin_response slot) let set_rcode slot rcode = Proven_error.from_status (c_dns_set_rcode slot rcode) +(* Enables the mode bit only; response construction rejects without a signer. *) let enable_dnssec slot = Proven_error.from_status (c_dns_enable_dnssec slot) +(* Always fails closed; the ABI carries an algorithm tag but no key bytes. *) let load_dnssec_key slot algo = Proven_error.from_status (c_dns_load_dnssec_key slot (algorithm_to_tag algo)) +(* Always fails closed because no DNSSEC signing backend is present. *) let sign_response slot = Proven_error.from_status (c_dns_sign_response slot) +(* The binding raises unavailable; no validation result is produced. *) let validate_dnssec slot = c_dns_validate_dnssec slot = 0 let can_transition ~from ~to_ = diff --git a/bindings/ocaml/lib/proven_doh.ml b/bindings/ocaml/lib/proven_doh.ml index 17867167..73b7dad5 100644 --- a/bindings/ocaml/lib/proven_doh.ml +++ b/bindings/ocaml/lib/proven_doh.ml @@ -91,13 +91,20 @@ let session_state_of_tag = function | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_doh_abi_version : unit -> int = "doh_abi_version" -external c_doh_create_context : unit -> int = "doh_create_context" -external c_doh_destroy_context : int -> unit = "doh_destroy_context" -external c_doh_state : int -> int = "doh_state" -external c_doh_can_transition : int -> int -> int = "doh_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_doh_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_doh_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_doh_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_doh_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_doh_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_doq.ml b/bindings/ocaml/lib/proven_doq.ml index 691ea5d8..b577da09 100644 --- a/bindings/ocaml/lib/proven_doq.ml +++ b/bindings/ocaml/lib/proven_doq.ml @@ -85,14 +85,22 @@ let server_state_of_tag = function | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_doq_abi_version : unit -> int = "doq_abi_version" -external c_doq_create_context : unit -> int = "doq_create_context" -external c_doq_destroy_context : int -> unit = "doq_destroy_context" -external c_doq_state : int -> int = "doq_state" -external c_doq_server_state : int -> int = "doq_server_state" -external c_doq_can_transition : int -> int -> int = "doq_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_doq_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_doq_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_doq_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_doq_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_doq_server_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_doq_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_dot.ml b/bindings/ocaml/lib/proven_dot.ml index d243ef85..ca26a465 100644 --- a/bindings/ocaml/lib/proven_dot.ml +++ b/bindings/ocaml/lib/proven_dot.ml @@ -87,14 +87,22 @@ let server_state_of_tag = function | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_dot_abi_version : unit -> int = "dot_abi_version" -external c_dot_create_context : unit -> int = "dot_create_context" -external c_dot_destroy_context : int -> unit = "dot_destroy_context" -external c_dot_state : int -> int = "dot_state" -external c_dot_server_state : int -> int = "dot_server_state" -external c_dot_can_transition : int -> int -> int = "dot_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_dot_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_dot_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_dot_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dot_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dot_server_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_dot_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_error.ml b/bindings/ocaml/lib/proven_error.ml index cd8930e0..76d36281 100644 --- a/bindings/ocaml/lib/proven_error.ml +++ b/bindings/ocaml/lib/proven_error.ml @@ -1,11 +1,11 @@ (* SPDX-License-Identifier: MPL-2.0 *) (* Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) *) -(** Shared error types for all proven-servers FFI operations. +(** Shared error types for the OCaml model scaffolds. - Every protocol FFI uses the same slot-based context pool pattern with - [int] return values (-1 = no slot, 0/1 = success/failure). This module - maps those patterns to a descriptive OCaml variant type. *) + The current native operations are disabled until OCaml-compatible C stubs + exist. These conversion helpers document the intended ABI conventions for + a future verified bridge; they do not make the current stubs callable. *) (** Unified error type for all proven-servers FFI operations. *) type t = diff --git a/bindings/ocaml/lib/proven_federation.ml b/bindings/ocaml/lib/proven_federation.ml index b80c531f..8e791d11 100644 --- a/bindings/ocaml/lib/proven_federation.ml +++ b/bindings/ocaml/lib/proven_federation.ml @@ -174,13 +174,20 @@ let server_state_of_tag = function | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_federation_abi_version : unit -> int = "federation_abi_version" -external c_federation_create_context : unit -> int = "federation_create_context" -external c_federation_destroy_context : int -> unit = "federation_destroy_context" -external c_federation_state : int -> int = "federation_state" -external c_federation_can_transition : int -> int -> int = "federation_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_federation_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_federation_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_federation_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_federation_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_federation_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_fileserver.ml b/bindings/ocaml/lib/proven_fileserver.ml index ce989042..2a5557b5 100644 --- a/bindings/ocaml/lib/proven_fileserver.ml +++ b/bindings/ocaml/lib/proven_fileserver.ml @@ -186,13 +186,20 @@ let session_state_of_tag = function | 4 -> Some Disconnecting | _ -> None -(* --- C FFI declarations --- *) - -external c_fileserver_abi_version : unit -> int = "fileserver_abi_version" -external c_fileserver_create_context : unit -> int = "fileserver_create_context" -external c_fileserver_destroy_context : int -> unit = "fileserver_destroy_context" -external c_fileserver_state : int -> int = "fileserver_state" -external c_fileserver_can_transition : int -> int -> int = "fileserver_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_fileserver_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_fileserver_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_fileserver_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fileserver_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fileserver_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_firewall.ml b/bindings/ocaml/lib/proven_firewall.ml index 4c2f7011..9592e2d0 100644 --- a/bindings/ocaml/lib/proven_firewall.ml +++ b/bindings/ocaml/lib/proven_firewall.ml @@ -58,34 +58,58 @@ let conntrack_state_of_tag = function | 0 -> Some Ct_none | 1 -> Some Ct_tracking | 2 -> Some Ct_established | 3 -> Some Ct_related | 4 -> Some Ct_expired | _ -> None -(* --- C FFI declarations --- *) - -external c_fw_abi_version : unit -> int = "fw_abi_version" -external c_fw_create_context : unit -> int = "fw_create_context" -external c_fw_destroy_context : int -> unit = "fw_destroy_context" -external c_fw_packet_state : int -> int = "fw_packet_state" -external c_fw_conntrack_state : int -> int = "fw_conntrack_state" -external c_fw_get_decision : int -> int = "fw_get_decision" -external c_fw_rule_count : int -> int = "fw_rule_count" -external c_fw_packet_proto : int -> int = "fw_packet_proto" -external c_fw_packet_chain : int -> int = "fw_packet_chain" -external c_fw_packet_src_ip : int -> int = "fw_packet_src_ip" -external c_fw_packet_dst_ip : int -> int = "fw_packet_dst_ip" -external c_fw_packet_src_port : int -> int = "fw_packet_src_port" -external c_fw_packet_dst_port : int -> int = "fw_packet_dst_port" -external c_fw_classify_packet : int -> int -> int -> int -> int -> int -> int -> int - = "fw_classify_packet_bytecode" "fw_classify_packet" -external c_fw_begin_chain : int -> int = "fw_begin_chain" -external c_fw_add_rule : int -> int -> int -> int -> int -> int - = "fw_add_rule_bytecode" "fw_add_rule" -external c_fw_set_default_action : int -> int -> int = "fw_set_default_action" -external c_fw_evaluate_rules : int -> int = "fw_evaluate_rules" -external c_fw_commit : int -> int = "fw_commit" -external c_fw_begin_tracking : int -> int = "fw_begin_tracking" -external c_fw_complete_tracking : int -> int -> int = "fw_complete_tracking" -external c_fw_expire_conn : int -> int = "fw_expire_conn" -external c_fw_can_transition : int -> int -> int = "fw_can_transition" -external c_fw_can_conntrack_transition : int -> int -> int = "fw_can_conntrack_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_fw_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_fw_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_fw_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_packet_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_conntrack_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_get_decision : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_rule_count : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_packet_proto : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_packet_chain : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_packet_src_ip : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_packet_dst_ip : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_packet_src_port : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_packet_dst_port : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_classify_packet : int -> int -> int -> int -> int -> int -> int -> int = fun _arg0 _arg1 _arg2 _arg3 _arg4 _arg5 _arg6 -> + Proven_unavailable.raise_unavailable () +let c_fw_begin_chain : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_add_rule : int -> int -> int -> int -> int -> int = fun _arg0 _arg1 _arg2 _arg3 _arg4 -> + Proven_unavailable.raise_unavailable () +let c_fw_set_default_action : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_fw_evaluate_rules : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_commit : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_begin_tracking : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_complete_tracking : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_fw_expire_conn : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_fw_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_fw_can_conntrack_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ftp.ml b/bindings/ocaml/lib/proven_ftp.ml index 1a35607c..e4e645b4 100644 --- a/bindings/ocaml/lib/proven_ftp.ml +++ b/bindings/ocaml/lib/proven_ftp.ml @@ -33,29 +33,52 @@ let transfer_state_of_tag = function | 0 -> Some Transfer_idle | 1 -> Some Transfer_in_progress | 2 -> Some Transfer_completed | 3 -> Some Transfer_aborted | _ -> None -(* --- C FFI declarations --- *) +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) -external c_ftp_abi_version : unit -> int = "ftp_abi_version" -external c_ftp_create : unit -> int = "ftp_create" -external c_ftp_destroy : int -> unit = "ftp_destroy" -external c_ftp_state : int -> int = "ftp_state" -external c_ftp_transfer_type : int -> int = "ftp_transfer_type" -external c_ftp_data_mode : int -> int = "ftp_data_mode" -external c_ftp_transfer_state : int -> int = "ftp_transfer_state" -external c_ftp_file_count : int -> int = "ftp_file_count" -external c_ftp_last_reply_code : int -> int = "ftp_last_reply_code" -external c_ftp_quit : int -> int = "ftp_quit" -external c_ftp_cdup : int -> int = "ftp_cdup" -external c_ftp_set_type : int -> int -> int = "ftp_set_type" -external c_ftp_set_passive : int -> int = "ftp_set_passive" -external c_ftp_set_active : int -> int -> int = "ftp_set_active" -external c_ftp_begin_transfer : int -> int = "ftp_begin_transfer" -external c_ftp_complete_transfer : int -> int = "ftp_complete_transfer" -external c_ftp_abort_transfer : int -> int = "ftp_abort_transfer" -external c_ftp_begin_rename : int -> int = "ftp_begin_rename" -external c_ftp_complete_rename : int -> int = "ftp_complete_rename" -external c_ftp_can_transfer : int -> int = "ftp_can_transfer" -external c_ftp_can_transition : int -> int -> int = "ftp_can_transition" +let c_ftp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ftp_create : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ftp_destroy : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_transfer_type : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_data_mode : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_transfer_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_file_count : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_last_reply_code : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_quit : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_cdup : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_set_type : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ftp_set_passive : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_set_active : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ftp_begin_transfer : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_complete_transfer : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_abort_transfer : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_begin_rename : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_complete_rename : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_can_transfer : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ftp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_gameserver.ml b/bindings/ocaml/lib/proven_gameserver.ml index 13205def..29f520f4 100644 --- a/bindings/ocaml/lib/proven_gameserver.ml +++ b/bindings/ocaml/lib/proven_gameserver.ml @@ -81,14 +81,22 @@ let match_state_of_tag = function | 5 -> Some Complete | _ -> None -(* --- C FFI declarations --- *) - -external c_gameserver_abi_version : unit -> int = "gameserver_abi_version" -external c_gameserver_create_context : unit -> int = "gameserver_create_context" -external c_gameserver_destroy_context : int -> unit = "gameserver_destroy_context" -external c_gameserver_state : int -> int = "gameserver_state" -external c_gameserver_match_state : int -> int = "gameserver_match_state" -external c_gameserver_can_transition : int -> int -> int = "gameserver_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_gameserver_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_gameserver_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_gameserver_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_gameserver_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_gameserver_match_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_gameserver_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_git.ml b/bindings/ocaml/lib/proven_git.ml index 8f0f891f..1088578c 100644 --- a/bindings/ocaml/lib/proven_git.ml +++ b/bindings/ocaml/lib/proven_git.ml @@ -143,13 +143,20 @@ let server_state_of_tag = function | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_git_abi_version : unit -> int = "git_abi_version" -external c_git_create_context : unit -> int = "git_create_context" -external c_git_destroy_context : int -> unit = "git_destroy_context" -external c_git_state : int -> int = "git_state" -external c_git_can_transition : int -> int -> int = "git_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_git_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_git_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_git_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_git_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_git_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_graphdb.ml b/bindings/ocaml/lib/proven_graphdb.ml index ab2ffb7b..1f938075 100644 --- a/bindings/ocaml/lib/proven_graphdb.ml +++ b/bindings/ocaml/lib/proven_graphdb.ml @@ -91,12 +91,18 @@ let session_state_of_tag = function | 0 -> Some Idle | 1 -> Some Connected | 2 -> Some Querying | 3 -> Some Traversing | 4 -> Some Disconnecting | _ -> None -(* --- C FFI declarations --- *) - -external c_graphdb_abi_version : unit -> int = "graphdb_abi_version" -external c_graphdb_create_context : unit -> int = "graphdb_create_context" -external c_graphdb_destroy_context : int -> unit = "graphdb_destroy_context" -external c_graphdb_can_transition : int -> int -> int = "graphdb_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_graphdb_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_graphdb_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_graphdb_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphdb_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_graphql.ml b/bindings/ocaml/lib/proven_graphql.ml index 70d6e27e..e09d8ace 100644 --- a/bindings/ocaml/lib/proven_graphql.ml +++ b/bindings/ocaml/lib/proven_graphql.ml @@ -28,32 +28,58 @@ let phase_of_tag = function let op_type_to_tag = function | Query -> 0 | Mutation -> 1 | Subscription -> 2 -(* --- C FFI declarations --- *) - -external c_graphql_abi_version : unit -> int = "graphql_abi_version" -external c_graphql_create : int -> int = "graphql_create" -external c_graphql_destroy : int -> unit = "graphql_destroy" -external c_graphql_phase : int -> int = "graphql_phase" -external c_graphql_operation_type : int -> int = "graphql_operation_type" -external c_graphql_error_category : int -> int = "graphql_error_category" -external c_graphql_advance : int -> int = "graphql_advance" -external c_graphql_abort : int -> int -> int = "graphql_abort" -external c_graphql_set_query_depth : int -> int -> int = "graphql_set_query_depth" -external c_graphql_query_depth : int -> int = "graphql_query_depth" -external c_graphql_set_complexity : int -> int -> int = "graphql_set_complexity" -external c_graphql_complexity : int -> int = "graphql_complexity" -external c_graphql_resolve_field : int -> int -> int -> int = "graphql_resolve_field" -external c_graphql_fields_resolved : int -> int = "graphql_fields_resolved" -external c_graphql_can_transition : int -> int -> int = "graphql_can_transition" -external c_graphql_sub_create : int -> int = "graphql_sub_create" -external c_graphql_sub_phase : int -> int = "graphql_sub_phase" -external c_graphql_sub_advance : int -> int = "graphql_sub_advance" -external c_graphql_sub_emit_event : int -> int = "graphql_sub_emit_event" -external c_graphql_sub_abort : int -> int = "graphql_sub_abort" -external c_graphql_sub_event_count : int -> int = "graphql_sub_event_count" -external c_graphql_introspection_query : int -> int -> int = "graphql_introspection_query" -external c_graphql_check_depth : int -> int -> int = "graphql_check_depth" -external c_graphql_check_complexity : int -> int -> int = "graphql_check_complexity" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_graphql_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_graphql_create : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_destroy : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_phase : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_operation_type : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_error_category : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_advance : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_abort : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_graphql_set_query_depth : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_graphql_query_depth : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_set_complexity : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_graphql_complexity : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_resolve_field : int -> int -> int -> int = fun _arg0 _arg1 _arg2 -> + Proven_unavailable.raise_unavailable () +let c_graphql_fields_resolved : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_graphql_sub_create : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_sub_phase : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_sub_advance : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_sub_emit_event : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_sub_abort : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_sub_event_count : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_graphql_introspection_query : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_graphql_check_depth : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_graphql_check_complexity : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_grpc.ml b/bindings/ocaml/lib/proven_grpc.ml index 69c352d6..365ad366 100644 --- a/bindings/ocaml/lib/proven_grpc.ml +++ b/bindings/ocaml/lib/proven_grpc.ml @@ -55,31 +55,56 @@ let status_code_of_code = function let compression_to_tag = function | Comp_none -> 0 | Comp_gzip -> 1 | Comp_deflate -> 2 -(* --- C FFI declarations --- *) - -external c_grpc_abi_version : unit -> int = "grpc_abi_version" -external c_grpc_create : int -> int = "grpc_create" -external c_grpc_destroy : int -> unit = "grpc_destroy" -external c_grpc_stream_state : int -> int = "grpc_stream_state" -external c_grpc_compression : int -> int = "grpc_compression" -external c_grpc_status_code : int -> int = "grpc_status_code" -external c_grpc_set_status : int -> int -> int = "grpc_set_status" -external c_grpc_stream_id : int -> int = "grpc_stream_id" -external c_grpc_send_headers : int -> int = "grpc_send_headers" -external c_grpc_local_end_stream : int -> int = "grpc_local_end_stream" -external c_grpc_remote_end_stream : int -> int = "grpc_remote_end_stream" -external c_grpc_reset_stream : int -> int -> int = "grpc_reset_stream" -external c_grpc_close_half_local : int -> int = "grpc_close_half_local" -external c_grpc_close_half_remote : int -> int = "grpc_close_half_remote" -external c_grpc_push_promise : int -> int = "grpc_push_promise" -external c_grpc_reserved_to_half : int -> int = "grpc_reserved_to_half" -external c_grpc_can_send : int -> int = "grpc_can_send" -external c_grpc_can_receive : int -> int = "grpc_can_receive" -external c_grpc_send_window : int -> int = "grpc_send_window" -external c_grpc_recv_window : int -> int = "grpc_recv_window" -external c_grpc_update_send_window : int -> int -> int = "grpc_update_send_window" -external c_grpc_update_recv_window : int -> int -> int = "grpc_update_recv_window" -external c_grpc_can_transition : int -> int -> int = "grpc_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_grpc_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_grpc_create : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_destroy : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_stream_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_compression : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_status_code : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_set_status : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_grpc_stream_id : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_send_headers : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_local_end_stream : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_remote_end_stream : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_reset_stream : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_grpc_close_half_local : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_close_half_remote : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_push_promise : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_reserved_to_half : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_can_send : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_can_receive : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_send_window : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_recv_window : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_grpc_update_send_window : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_grpc_update_recv_window : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_grpc_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_hardened.ml b/bindings/ocaml/lib/proven_hardened.ml index d8309d07..328a33f5 100644 --- a/bindings/ocaml/lib/proven_hardened.ml +++ b/bindings/ocaml/lib/proven_hardened.ml @@ -91,12 +91,18 @@ let server_state_of_tag = function | 0 -> Some Idle | 1 -> Some Hardening | 2 -> Some Active | 3 -> Some Auditing | 4 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_hardened_abi_version : unit -> int = "hardened_abi_version" -external c_hardened_create_context : unit -> int = "hardened_create_context" -external c_hardened_destroy_context : int -> unit = "hardened_destroy_context" -external c_hardened_can_transition : int -> int -> int = "hardened_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_hardened_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_hardened_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_hardened_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_hardened_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_honeypot.ml b/bindings/ocaml/lib/proven_honeypot.ml index ab173062..7b613595 100644 --- a/bindings/ocaml/lib/proven_honeypot.ml +++ b/bindings/ocaml/lib/proven_honeypot.ml @@ -75,12 +75,18 @@ let server_state_of_tag = function | 0 -> Some Idle | 1 -> Some Deployed | 2 -> Some Engaged | 3 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_honeypot_abi_version : unit -> int = "honeypot_abi_version" -external c_honeypot_create_context : unit -> int = "honeypot_create_context" -external c_honeypot_destroy_context : int -> unit = "honeypot_destroy_context" -external c_honeypot_can_transition : int -> int -> int = "honeypot_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_honeypot_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_honeypot_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_honeypot_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_honeypot_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_http.ml b/bindings/ocaml/lib/proven_http.ml index e9253e2e..41c6419b 100644 --- a/bindings/ocaml/lib/proven_http.ml +++ b/bindings/ocaml/lib/proven_http.ml @@ -138,12 +138,18 @@ let request_phase_of_tag = function | 3 -> Some BodyReceiving | 4 -> Some Complete | 5 -> Some Responding | 6 -> Some Sent | _ -> None -(* --- C FFI declarations --- *) - -external c_http_abi_version : unit -> int = "http_abi_version" -external c_http_create_context : unit -> int = "http_create_context" -external c_http_destroy_context : int -> unit = "http_destroy_context" -external c_http_can_transition : int -> int -> int = "http_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_http_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_http_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_http_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_http_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_httpd.ml b/bindings/ocaml/lib/proven_httpd.ml index 252844f7..0eaa36b7 100644 --- a/bindings/ocaml/lib/proven_httpd.ml +++ b/bindings/ocaml/lib/proven_httpd.ml @@ -65,20 +65,34 @@ let phase_of_tag = function let version_of_tag = function | 0 -> Some Http10 | 1 -> Some Http11 | _ -> None -(* --- C FFI declarations --- *) - -external c_http_abi_version : unit -> int = "http_abi_version" -external c_http_create_context : unit -> int = "http_create_context" -external c_http_destroy_context : int -> unit = "http_destroy_context" -external c_http_parse_request : int -> int = "http_parse_request" -external c_http_get_method : int -> int = "http_get_method" -external c_http_set_status : int -> int -> int = "http_set_status" -external c_http_send_response : int -> int = "http_send_response" -external c_http_keep_alive_check : int -> int = "http_keep_alive_check" -external c_http_get_phase : int -> int = "http_get_phase" -external c_http_get_version : int -> int = "http_get_version" -external c_http_reset_context : int -> int = "http_reset_context" -external c_http_can_transition : int -> int -> int = "http_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_http_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_http_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_http_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_http_parse_request : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_http_get_method : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_http_set_status : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_http_send_response : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_http_keep_alive_check : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_http_get_phase : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_http_get_version : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_http_reset_context : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_http_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ids.ml b/bindings/ocaml/lib/proven_ids.ml index a04a8314..18426f18 100644 --- a/bindings/ocaml/lib/proven_ids.ml +++ b/bindings/ocaml/lib/proven_ids.ml @@ -91,12 +91,18 @@ let threat_level_of_tag = function | 0 -> Some Info | 1 -> Some ThreatLevel_Low | 2 -> Some ThreatLevel_Medium | 3 -> Some ThreatLevel_High | 4 -> Some ThreatLevel_Critical | _ -> None -(* --- C FFI declarations --- *) - -external c_ids_abi_version : unit -> int = "ids_abi_version" -external c_ids_create_context : unit -> int = "ids_create_context" -external c_ids_destroy_context : int -> unit = "ids_destroy_context" -external c_ids_can_transition : int -> int -> int = "ids_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ids_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ids_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ids_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ids_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_imap.ml b/bindings/ocaml/lib/proven_imap.ml index 798ae330..c69d96fb 100644 --- a/bindings/ocaml/lib/proven_imap.ml +++ b/bindings/ocaml/lib/proven_imap.ml @@ -54,12 +54,18 @@ let flag_of_tag = function | 0 -> Some Seen | 1 -> Some Answered | 2 -> Some Flagged | 3 -> Some Deleted | 4 -> Some Draft | 5 -> Some Recent | _ -> None -(* --- C FFI declarations --- *) - -external c_imap_abi_version : unit -> int = "imap_abi_version" -external c_imap_create_context : unit -> int = "imap_create_context" -external c_imap_destroy_context : int -> unit = "imap_destroy_context" -external c_imap_can_transition : int -> int -> int = "imap_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_imap_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_imap_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_imap_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_imap_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_irc.ml b/bindings/ocaml/lib/proven_irc.ml index 8b26d5bb..33b55ed4 100644 --- a/bindings/ocaml/lib/proven_irc.ml +++ b/bindings/ocaml/lib/proven_irc.ml @@ -95,12 +95,18 @@ let irc_error_of_tag = function | 3 -> Some IrcError_InviteOnly | 4 -> Some Banned | 5 -> Some NotRegistered | _ -> Option.None -(* --- C FFI declarations --- *) - -external c_irc_abi_version : unit -> int = "irc_abi_version" -external c_irc_create_context : unit -> int = "irc_create_context" -external c_irc_destroy_context : int -> unit = "irc_destroy_context" -external c_irc_can_transition : int -> int -> int = "irc_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_irc_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_irc_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_irc_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_irc_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_kerberos.ml b/bindings/ocaml/lib/proven_kerberos.ml index 0ee0efdc..78d63241 100644 --- a/bindings/ocaml/lib/proven_kerberos.ml +++ b/bindings/ocaml/lib/proven_kerberos.ml @@ -147,12 +147,18 @@ let negotiation_state_of_tag = function | 0 -> Some NegIdle | 1 -> Some Proposed | 2 -> Some Selected | 3 -> Some NegFailed | _ -> None -(* --- C FFI declarations --- *) - -external c_kerberos_abi_version : unit -> int = "kerberos_abi_version" -external c_kerberos_create_context : unit -> int = "kerberos_create_context" -external c_kerberos_destroy_context : int -> unit = "kerberos_destroy_context" -external c_kerberos_can_transition : int -> int -> int = "kerberos_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_kerberos_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_kerberos_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_kerberos_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_kerberos_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_kms.ml b/bindings/ocaml/lib/proven_kms.ml index 8759d0cc..090bd01f 100644 --- a/bindings/ocaml/lib/proven_kms.ml +++ b/bindings/ocaml/lib/proven_kms.ml @@ -77,12 +77,18 @@ let kms_algorithm_of_tag = function | 6 -> Some Ed25519 | 7 -> Some Chacha20Poly1305 | 8 -> Some HmacSha256 | _ -> None -(* --- C FFI declarations --- *) - -external c_kms_abi_version : unit -> int = "kms_abi_version" -external c_kms_create_context : unit -> int = "kms_create_context" -external c_kms_destroy_context : int -> unit = "kms_destroy_context" -external c_kms_can_transition : int -> int -> int = "kms_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_kms_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_kms_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_kms_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_kms_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ldap.ml b/bindings/ocaml/lib/proven_ldap.ml index 448698b0..9b749364 100644 --- a/bindings/ocaml/lib/proven_ldap.ml +++ b/bindings/ocaml/lib/proven_ldap.ml @@ -72,12 +72,18 @@ let result_code_of_tag = function | 7 -> Some InvalidCredentials | 8 -> Some InsufficientAccessRights | 9 -> Some Busy | 10 -> Some Unavailable | _ -> None -(* --- C FFI declarations --- *) - -external c_ldap_abi_version : unit -> int = "ldap_abi_version" -external c_ldap_create_context : unit -> int = "ldap_create_context" -external c_ldap_destroy_context : int -> unit = "ldap_destroy_context" -external c_ldap_can_transition : int -> int -> int = "ldap_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ldap_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ldap_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ldap_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ldap_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ldp.ml b/bindings/ocaml/lib/proven_ldp.ml index 33c9c11a..b6ec142d 100644 --- a/bindings/ocaml/lib/proven_ldp.ml +++ b/bindings/ocaml/lib/proven_ldp.ml @@ -80,12 +80,18 @@ let constraint_violation_of_tag = function | 0 -> Some MembershipConstant | 1 -> Some ContainsTriplesModified | 2 -> Some ServerManaged | 3 -> Some TypeConflict | _ -> None -(* --- C FFI declarations --- *) - -external c_ldp_abi_version : unit -> int = "ldp_abi_version" -external c_ldp_create_context : unit -> int = "ldp_create_context" -external c_ldp_destroy_context : int -> unit = "ldp_destroy_context" -external c_ldp_can_transition : int -> int -> int = "ldp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ldp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ldp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ldp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ldp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_loadbalancer.ml b/bindings/ocaml/lib/proven_loadbalancer.ml index 3646fe86..62ae73d5 100644 --- a/bindings/ocaml/lib/proven_loadbalancer.ml +++ b/bindings/ocaml/lib/proven_loadbalancer.ml @@ -79,12 +79,18 @@ let lb_protocol_of_tag = function | 0 -> Some LbProtocol_Http | 1 -> Some Https | 2 -> Some LbProtocol_Tcp | 3 -> Some Udp | 4 -> Some LbProtocol_Grpc | _ -> None -(* --- C FFI declarations --- *) - -external c_loadbalancer_abi_version : unit -> int = "loadbalancer_abi_version" -external c_loadbalancer_create_context : unit -> int = "loadbalancer_create_context" -external c_loadbalancer_destroy_context : int -> unit = "loadbalancer_destroy_context" -external c_loadbalancer_can_transition : int -> int -> int = "loadbalancer_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_loadbalancer_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_loadbalancer_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_loadbalancer_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_loadbalancer_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_logcollector.ml b/bindings/ocaml/lib/proven_logcollector.ml index 6c88c786..29b28dd6 100644 --- a/bindings/ocaml/lib/proven_logcollector.ml +++ b/bindings/ocaml/lib/proven_logcollector.ml @@ -74,12 +74,18 @@ let pipeline_stage_of_tag = function | 0 -> Some Input | 1 -> Some Parse | 2 -> Some Filter | 3 -> Some PipelineTransform | 4 -> Some Output | _ -> None -(* --- C FFI declarations --- *) - -external c_logcollector_abi_version : unit -> int = "logcollector_abi_version" -external c_logcollector_create_context : unit -> int = "logcollector_create_context" -external c_logcollector_destroy_context : int -> unit = "logcollector_destroy_context" -external c_logcollector_can_transition : int -> int -> int = "logcollector_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_logcollector_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_logcollector_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_logcollector_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_logcollector_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_lpd.ml b/bindings/ocaml/lib/proven_lpd.ml index 7e44c338..d071dfda 100644 --- a/bindings/ocaml/lib/proven_lpd.ml +++ b/bindings/ocaml/lib/proven_lpd.ml @@ -48,12 +48,18 @@ let job_status_of_tag = function | 0 -> Some Pending | 1 -> Some Printing | 2 -> Some Complete | 3 -> Some Failed | _ -> None -(* --- C FFI declarations --- *) - -external c_lpd_abi_version : unit -> int = "lpd_abi_version" -external c_lpd_create_context : unit -> int = "lpd_create_context" -external c_lpd_destroy_context : int -> unit = "lpd_destroy_context" -external c_lpd_can_transition : int -> int -> int = "lpd_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_lpd_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_lpd_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_lpd_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_lpd_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_mcp.ml b/bindings/ocaml/lib/proven_mcp.ml index 6dbeae81..1fb8f15b 100644 --- a/bindings/ocaml/lib/proven_mcp.ml +++ b/bindings/ocaml/lib/proven_mcp.ml @@ -99,12 +99,18 @@ let session_state_of_tag = function | 0 -> Some Idle | 1 -> Some Connecting | 2 -> Some Ready | 3 -> Some Processing | 4 -> Some Disconnecting | _ -> None -(* --- C FFI declarations --- *) - -external c_mcp_abi_version : unit -> int = "mcp_abi_version" -external c_mcp_create_context : unit -> int = "mcp_create_context" -external c_mcp_destroy_context : int -> unit = "mcp_destroy_context" -external c_mcp_can_transition : int -> int -> int = "mcp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_mcp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_mcp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_mcp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_mcp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_mdns.ml b/bindings/ocaml/lib/proven_mdns.ml index cf8db37d..94da203b 100644 --- a/bindings/ocaml/lib/proven_mdns.ml +++ b/bindings/ocaml/lib/proven_mdns.ml @@ -72,12 +72,18 @@ let responder_state_of_tag = function | 0 -> Some Idle | 1 -> Some Probing | 2 -> Some Announcing | 3 -> Some Running | 4 -> Some ShuttingDown | _ -> None -(* --- C FFI declarations --- *) - -external c_mdns_abi_version : unit -> int = "mdns_abi_version" -external c_mdns_create_context : unit -> int = "mdns_create_context" -external c_mdns_destroy_context : int -> unit = "mdns_destroy_context" -external c_mdns_can_transition : int -> int -> int = "mdns_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_mdns_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_mdns_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_mdns_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_mdns_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_media.ml b/bindings/ocaml/lib/proven_media.ml index 09b31dec..a4f64a5c 100644 --- a/bindings/ocaml/lib/proven_media.ml +++ b/bindings/ocaml/lib/proven_media.ml @@ -91,12 +91,18 @@ let player_state_of_tag = function | 0 -> Some Idle | 1 -> Some Ready | 2 -> Some Playing | 3 -> Some Paused | 4 -> Some Stopping | _ -> None -(* --- C FFI declarations --- *) - -external c_media_abi_version : unit -> int = "media_abi_version" -external c_media_create_context : unit -> int = "media_create_context" -external c_media_destroy_context : int -> unit = "media_destroy_context" -external c_media_can_transition : int -> int -> int = "media_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_media_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_media_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_media_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_media_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_metrics.ml b/bindings/ocaml/lib/proven_metrics.ml index 84543a0a..30e990bd 100644 --- a/bindings/ocaml/lib/proven_metrics.ml +++ b/bindings/ocaml/lib/proven_metrics.ml @@ -93,12 +93,18 @@ let collector_state_of_tag = function | 0 -> Some Idle | 1 -> Some Configured | 2 -> Some Scraping | 3 -> Some Alerting | 4 -> Some Stopping | _ -> None -(* --- C FFI declarations --- *) - -external c_metrics_abi_version : unit -> int = "metrics_abi_version" -external c_metrics_create_context : unit -> int = "metrics_create_context" -external c_metrics_destroy_context : int -> unit = "metrics_destroy_context" -external c_metrics_can_transition : int -> int -> int = "metrics_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_metrics_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_metrics_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_metrics_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_metrics_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_modbus.ml b/bindings/ocaml/lib/proven_modbus.ml index 0840c6d4..455a6a67 100644 --- a/bindings/ocaml/lib/proven_modbus.ml +++ b/bindings/ocaml/lib/proven_modbus.ml @@ -78,12 +78,18 @@ let gateway_state_of_tag = function | 0 -> Some Idle | 1 -> Some Listening | 2 -> Some Processing | 3 -> Some Error | 4 -> Some Stopping | _ -> None -(* --- C FFI declarations --- *) - -external c_modbus_abi_version : unit -> int = "modbus_abi_version" -external c_modbus_create_context : unit -> int = "modbus_create_context" -external c_modbus_destroy_context : int -> unit = "modbus_destroy_context" -external c_modbus_can_transition : int -> int -> int = "modbus_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_modbus_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_modbus_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_modbus_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_modbus_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_monitor.ml b/bindings/ocaml/lib/proven_monitor.ml index 18399452..e1eb471b 100644 --- a/bindings/ocaml/lib/proven_monitor.ml +++ b/bindings/ocaml/lib/proven_monitor.ml @@ -94,12 +94,18 @@ let monitor_state_of_tag = function | 3 -> Some MonPaused | 4 -> Some Alerting | 5 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_monitor_abi_version : unit -> int = "monitor_abi_version" -external c_monitor_create_context : unit -> int = "monitor_create_context" -external c_monitor_destroy_context : int -> unit = "monitor_destroy_context" -external c_monitor_can_transition : int -> int -> int = "monitor_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_monitor_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_monitor_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_monitor_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_monitor_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_mqtt.ml b/bindings/ocaml/lib/proven_mqtt.ml index 1f79d392..545333be 100644 --- a/bindings/ocaml/lib/proven_mqtt.ml +++ b/bindings/ocaml/lib/proven_mqtt.ml @@ -28,26 +28,46 @@ let state_of_tag = function let qos_to_code = function | QoS0 -> 0 | QoS1 -> 1 | QoS2 -> 2 -(* --- C FFI declarations --- *) - -external c_mqtt_abi_version : unit -> int = "mqtt_abi_version" -external c_mqtt_create : int -> int -> int -> int = "mqtt_create" -external c_mqtt_destroy : int -> unit = "mqtt_destroy" -external c_mqtt_state : int -> int = "mqtt_state" -external c_mqtt_version : int -> int = "mqtt_version" -external c_mqtt_can_publish : int -> int = "mqtt_can_publish" -external c_mqtt_can_subscribe : int -> int = "mqtt_can_subscribe" -external c_mqtt_subscription_count : int -> int = "mqtt_subscription_count" -external c_mqtt_puback : int -> int -> int = "mqtt_puback" -external c_mqtt_pubrec : int -> int -> int = "mqtt_pubrec" -external c_mqtt_pubrel : int -> int -> int = "mqtt_pubrel" -external c_mqtt_pubcomp : int -> int -> int = "mqtt_pubcomp" -external c_mqtt_qos_state : int -> int -> int = "mqtt_qos_state" -external c_mqtt_disconnect : int -> int = "mqtt_disconnect" -external c_mqtt_cleanup : int -> int = "mqtt_cleanup" -external c_mqtt_retained_count : unit -> int = "mqtt_retained_count" -external c_mqtt_can_transition : int -> int -> int = "mqtt_can_transition" -external c_mqtt_qos_can_transition : int -> int -> int -> int = "mqtt_qos_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_mqtt_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_mqtt_create : int -> int -> int -> int = fun _arg0 _arg1 _arg2 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_destroy : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_version : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_can_publish : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_can_subscribe : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_subscription_count : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_puback : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_pubrec : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_pubrel : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_pubcomp : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_qos_state : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_disconnect : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_cleanup : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_retained_count : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_mqtt_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_mqtt_qos_can_transition : int -> int -> int -> int = fun _arg0 _arg1 _arg2 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_nesy.ml b/bindings/ocaml/lib/proven_nesy.ml index 02532651..17979559 100644 --- a/bindings/ocaml/lib/proven_nesy.ml +++ b/bindings/ocaml/lib/proven_nesy.ml @@ -113,12 +113,18 @@ let nesy_state_of_tag = function | 0 -> Some Idle | 1 -> Some Ready | 2 -> Some Reasoning | 3 -> Some Verifying | 4 -> Some Drift | 5 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_nesy_abi_version : unit -> int = "nesy_abi_version" -external c_nesy_create_context : unit -> int = "nesy_create_context" -external c_nesy_destroy_context : int -> unit = "nesy_destroy_context" -external c_nesy_can_transition : int -> int -> int = "nesy_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_nesy_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_nesy_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_nesy_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_nesy_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_netconf.ml b/bindings/ocaml/lib/proven_netconf.ml index 6af62abb..775e976c 100644 --- a/bindings/ocaml/lib/proven_netconf.ml +++ b/bindings/ocaml/lib/proven_netconf.ml @@ -92,12 +92,18 @@ let netconf_state_of_tag = function | 3 -> Some Editing | 4 -> Some Closing | 5 -> Some Terminated | _ -> None -(* --- C FFI declarations --- *) - -external c_netconf_abi_version : unit -> int = "netconf_abi_version" -external c_netconf_create_context : unit -> int = "netconf_create_context" -external c_netconf_destroy_context : int -> unit = "netconf_destroy_context" -external c_netconf_can_transition : int -> int -> int = "netconf_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_netconf_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_netconf_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_netconf_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_netconf_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_neurosym.ml b/bindings/ocaml/lib/proven_neurosym.ml index f6557bbc..c8d6b3b1 100644 --- a/bindings/ocaml/lib/proven_neurosym.ml +++ b/bindings/ocaml/lib/proven_neurosym.ml @@ -109,12 +109,18 @@ let neurosym_state_of_tag = function | 0 -> Some Idle | 1 -> Some Ready | 2 -> Some Inferring | 3 -> Some Reasoning | 4 -> Some Fusing | 5 -> Some Shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_neurosym_abi_version : unit -> int = "neurosym_abi_version" -external c_neurosym_create_context : unit -> int = "neurosym_create_context" -external c_neurosym_destroy_context : int -> unit = "neurosym_destroy_context" -external c_neurosym_can_transition : int -> int -> int = "neurosym_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_neurosym_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_neurosym_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_neurosym_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_neurosym_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_nfs.ml b/bindings/ocaml/lib/proven_nfs.ml index 26fcf39e..98bb954d 100644 --- a/bindings/ocaml/lib/proven_nfs.ml +++ b/bindings/ocaml/lib/proven_nfs.ml @@ -76,12 +76,18 @@ let nfs_state_of_tag = function | 0 -> Some Idle | 1 -> Some Mounted | 2 -> Some FileOpen | 3 -> Some Locked | 4 -> Some Busy | 5 -> Some Unmounting | _ -> None -(* --- C FFI declarations --- *) - -external c_nfs_abi_version : unit -> int = "nfs_abi_version" -external c_nfs_create_context : unit -> int = "nfs_create_context" -external c_nfs_destroy_context : int -> unit = "nfs_destroy_context" -external c_nfs_can_transition : int -> int -> int = "nfs_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_nfs_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_nfs_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_nfs_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_nfs_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ntp.ml b/bindings/ocaml/lib/proven_ntp.ml index b0d04ba1..eb2879cd 100644 --- a/bindings/ocaml/lib/proven_ntp.ml +++ b/bindings/ocaml/lib/proven_ntp.ml @@ -96,12 +96,18 @@ let ntp_error_of_tag = function | 3 -> Some InvalidPacket | 4 -> Some KissOfDeath | 5 -> Some StratumTooHigh | _ -> None -(* --- C FFI declarations --- *) - -external c_ntp_abi_version : unit -> int = "ntp_abi_version" -external c_ntp_create_context : unit -> int = "ntp_create_context" -external c_ntp_destroy_context : int -> unit = "ntp_destroy_context" -external c_ntp_can_transition : int -> int -> int = "ntp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ntp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ntp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ntp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ntp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_nts.ml b/bindings/ocaml/lib/proven_nts.ml index 3ebf83ef..0b7081cf 100644 --- a/bindings/ocaml/lib/proven_nts.ml +++ b/bindings/ocaml/lib/proven_nts.ml @@ -79,12 +79,18 @@ let session_state_of_tag = function | 0 -> Some Idle | 1 -> Some Handshaking | 2 -> Some SessionNegotiating | 3 -> Some SessionEstablished | 4 -> Some Closing | _ -> None -(* --- C FFI declarations --- *) - -external c_nts_abi_version : unit -> int = "nts_abi_version" -external c_nts_create_context : unit -> int = "nts_create_context" -external c_nts_destroy_context : int -> unit = "nts_destroy_context" -external c_nts_can_transition : int -> int -> int = "nts_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_nts_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_nts_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_nts_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_nts_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_objectstore.ml b/bindings/ocaml/lib/proven_objectstore.ml index e9d6a2dc..d2ea577a 100644 --- a/bindings/ocaml/lib/proven_objectstore.ml +++ b/bindings/ocaml/lib/proven_objectstore.ml @@ -89,12 +89,18 @@ let session_state_of_tag = function | 0 -> Some Idle | 1 -> Some Ready | 2 -> Some BucketActive | 3 -> Some Uploading | 4 -> Some Closing | _ -> None -(* --- C FFI declarations --- *) - -external c_objectstore_abi_version : unit -> int = "objectstore_abi_version" -external c_objectstore_create_context : unit -> int = "objectstore_create_context" -external c_objectstore_destroy_context : int -> unit = "objectstore_destroy_context" -external c_objectstore_can_transition : int -> int -> int = "objectstore_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_objectstore_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_objectstore_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_objectstore_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_objectstore_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ocsp.ml b/bindings/ocaml/lib/proven_ocsp.ml index 1e4650bb..b18d3e79 100644 --- a/bindings/ocaml/lib/proven_ocsp.ml +++ b/bindings/ocaml/lib/proven_ocsp.ml @@ -62,12 +62,18 @@ let responder_state_of_tag = function | 0 -> Some Idle | 1 -> Some Ready | 2 -> Some Processing | 3 -> Some Signing | 4 -> Some Closing | _ -> None -(* --- C FFI declarations --- *) - -external c_ocsp_abi_version : unit -> int = "ocsp_abi_version" -external c_ocsp_create_context : unit -> int = "ocsp_create_context" -external c_ocsp_destroy_context : int -> unit = "ocsp_destroy_context" -external c_ocsp_can_transition : int -> int -> int = "ocsp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ocsp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ocsp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ocsp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ocsp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_odns.ml b/bindings/ocaml/lib/proven_odns.ml index ad60b21c..1ed235a4 100644 --- a/bindings/ocaml/lib/proven_odns.ml +++ b/bindings/ocaml/lib/proven_odns.ml @@ -74,12 +74,18 @@ let session_state_of_tag = function | 0 -> Some Idle | 1 -> Some KeyExchange | 2 -> Some Ready | 3 -> Some Processing | 4 -> Some Closing | _ -> None -(* --- C FFI declarations --- *) - -external c_odns_abi_version : unit -> int = "odns_abi_version" -external c_odns_create_context : unit -> int = "odns_create_context" -external c_odns_destroy_context : int -> unit = "odns_destroy_context" -external c_odns_can_transition : int -> int -> int = "odns_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_odns_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_odns_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_odns_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_odns_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_opcua.ml b/bindings/ocaml/lib/proven_opcua.ml index 97d4fb47..a7f1b251 100644 --- a/bindings/ocaml/lib/proven_opcua.ml +++ b/bindings/ocaml/lib/proven_opcua.ml @@ -95,12 +95,18 @@ let session_state_of_tag = function | 3 -> Some Activated | 4 -> Some Monitoring | 5 -> Some Closing | _ -> None -(* --- C FFI declarations --- *) - -external c_opcua_abi_version : unit -> int = "opcua_abi_version" -external c_opcua_create_context : unit -> int = "opcua_create_context" -external c_opcua_destroy_context : int -> unit = "opcua_destroy_context" -external c_opcua_can_transition : int -> int -> int = "opcua_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_opcua_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_opcua_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_opcua_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_opcua_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ospf.ml b/bindings/ocaml/lib/proven_ospf.ml index cd778406..82396536 100644 --- a/bindings/ocaml/lib/proven_ospf.ml +++ b/bindings/ocaml/lib/proven_ospf.ml @@ -82,12 +82,18 @@ let ospf_error_of_tag = function | 3 -> Some Invalid_transition | 4 -> Some Invalid_packet | 5 -> Some Area_error | 6 -> Some Flood_limit | _ -> None -(* --- C FFI declarations --- *) - -external c_ospf_abi_version : unit -> int = "ospf_abi_version" -external c_ospf_create_context : unit -> int = "ospf_create_context" -external c_ospf_destroy_context : int -> unit = "ospf_destroy_context" -external c_ospf_can_transition : int -> int -> int = "ospf_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ospf_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ospf_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ospf_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ospf_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_pop3.ml b/bindings/ocaml/lib/proven_pop3.ml index ad248a37..a7a352e7 100644 --- a/bindings/ocaml/lib/proven_pop3.ml +++ b/bindings/ocaml/lib/proven_pop3.ml @@ -63,12 +63,18 @@ let pop3_error_of_tag = function | 3 -> Some Invalid_transition | 4 -> Some Invalid_command | 5 -> Some Auth_failed | _ -> None -(* --- C FFI declarations --- *) - -external c_pop3_abi_version : unit -> int = "pop3_abi_version" -external c_pop3_create_context : unit -> int = "pop3_create_context" -external c_pop3_destroy_context : int -> unit = "pop3_destroy_context" -external c_pop3_can_transition : int -> int -> int = "pop3_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_pop3_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_pop3_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_pop3_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_pop3_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_pqc.ml b/bindings/ocaml/lib/proven_pqc.ml index b5fe39b4..2cb99a6e 100644 --- a/bindings/ocaml/lib/proven_pqc.ml +++ b/bindings/ocaml/lib/proven_pqc.ml @@ -86,12 +86,18 @@ let key_state_of_tag = function | 0 -> Some Empty | 1 -> Some Generating | 2 -> Some Generated | 3 -> Some Active | 4 -> Some Expired | 5 -> Some Compromised | _ -> None -(* --- C FFI declarations --- *) - -external c_pqc_abi_version : unit -> int = "pqc_abi_version" -external c_pqc_create_context : unit -> int = "pqc_create_context" -external c_pqc_destroy_context : int -> unit = "pqc_destroy_context" -external c_pqc_can_transition : int -> int -> int = "pqc_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_pqc_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_pqc_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_pqc_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_pqc_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_proxy.ml b/bindings/ocaml/lib/proven_proxy.ml index 3c64fd9f..7f184125 100644 --- a/bindings/ocaml/lib/proven_proxy.ml +++ b/bindings/ocaml/lib/proven_proxy.ml @@ -62,12 +62,18 @@ let proxy_error_of_tag = function | 0 -> Some Bad_gateway | 1 -> Some Gateway_timeout | 2 -> Some Upstream_refused | 3 -> Some Upstream_tls | _ -> None -(* --- C FFI declarations --- *) - -external c_proxy_abi_version : unit -> int = "proxy_abi_version" -external c_proxy_create_context : unit -> int = "proxy_create_context" -external c_proxy_destroy_context : int -> unit = "proxy_destroy_context" -external c_proxy_can_transition : int -> int -> int = "proxy_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_proxy_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_proxy_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_proxy_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_proxy_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ptp.ml b/bindings/ocaml/lib/proven_ptp.ml index f7933e1a..20cd363f 100644 --- a/bindings/ocaml/lib/proven_ptp.ml +++ b/bindings/ocaml/lib/proven_ptp.ml @@ -67,12 +67,18 @@ let delay_mechanism_to_tag = function let delay_mechanism_of_tag = function | 0 -> Some E2e | 1 -> Some P2p | 2 -> Some Dm_disabled | _ -> None -(* --- C FFI declarations --- *) - -external c_ptp_abi_version : unit -> int = "ptp_abi_version" -external c_ptp_create_context : unit -> int = "ptp_create_context" -external c_ptp_destroy_context : int -> unit = "ptp_destroy_context" -external c_ptp_can_transition : int -> int -> int = "ptp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ptp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ptp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ptp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ptp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_radius.ml b/bindings/ocaml/lib/proven_radius.ml index 3da0c557..447d1de4 100644 --- a/bindings/ocaml/lib/proven_radius.ml +++ b/bindings/ocaml/lib/proven_radius.ml @@ -100,12 +100,18 @@ let radius_result_of_tag = function | 0 -> Some Ok | 1 -> Some Err | 2 -> Some Invalid_param | 3 -> Some Pool_exhausted | 4 -> Some Bad_secret | _ -> None -(* --- C FFI declarations --- *) - -external c_radius_abi_version : unit -> int = "radius_abi_version" -external c_radius_create_context : unit -> int = "radius_create_context" -external c_radius_destroy_context : int -> unit = "radius_destroy_context" -external c_radius_can_transition : int -> int -> int = "radius_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_radius_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_radius_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_radius_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_radius_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_rtsp.ml b/bindings/ocaml/lib/proven_rtsp.ml index ab43812d..8bb4d414 100644 --- a/bindings/ocaml/lib/proven_rtsp.ml +++ b/bindings/ocaml/lib/proven_rtsp.ml @@ -91,12 +91,18 @@ let rtsp_error_of_tag = function | 3 -> Some Invalid_transition | 4 -> Some Rtsp_method_not_allowed | 5 -> Some Transport_error | 6 -> Some Session_expired | _ -> None -(* --- C FFI declarations --- *) - -external c_rtsp_abi_version : unit -> int = "rtsp_abi_version" -external c_rtsp_create_context : unit -> int = "rtsp_create_context" -external c_rtsp_destroy_context : int -> unit = "rtsp_destroy_context" -external c_rtsp_can_transition : int -> int -> int = "rtsp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_rtsp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_rtsp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_rtsp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_rtsp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_sandbox.ml b/bindings/ocaml/lib/proven_sandbox.ml index 41f59ace..6f9fa4b9 100644 --- a/bindings/ocaml/lib/proven_sandbox.ml +++ b/bindings/ocaml/lib/proven_sandbox.ml @@ -77,12 +77,18 @@ let syscall_policy_of_tag = function | 0 -> Some Allow | 1 -> Some Deny | 2 -> Some Log | 3 -> Some Trap | _ -> None -(* --- C FFI declarations --- *) - -external c_sandbox_abi_version : unit -> int = "sandbox_abi_version" -external c_sandbox_create_context : unit -> int = "sandbox_create_context" -external c_sandbox_destroy_context : int -> unit = "sandbox_destroy_context" -external c_sandbox_can_transition : int -> int -> int = "sandbox_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_sandbox_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_sandbox_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_sandbox_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_sandbox_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_sdn.ml b/bindings/ocaml/lib/proven_sdn.ml index f2442fe6..d48c6cd6 100644 --- a/bindings/ocaml/lib/proven_sdn.ml +++ b/bindings/ocaml/lib/proven_sdn.ml @@ -74,12 +74,18 @@ let port_state_to_tag = function let port_state_of_tag = function | 0 -> Some Up | 1 -> Some Down | 2 -> Some Blocked | _ -> None -(* --- C FFI declarations --- *) - -external c_sdn_abi_version : unit -> int = "sdn_abi_version" -external c_sdn_create_context : unit -> int = "sdn_create_context" -external c_sdn_destroy_context : int -> unit = "sdn_destroy_context" -external c_sdn_can_transition : int -> int -> int = "sdn_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_sdn_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_sdn_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_sdn_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_sdn_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_semweb.ml b/bindings/ocaml/lib/proven_semweb.ml index c034ff8c..cd5f7cbe 100644 --- a/bindings/ocaml/lib/proven_semweb.ml +++ b/bindings/ocaml/lib/proven_semweb.ml @@ -74,12 +74,18 @@ let semweb_error_code_of_tag = function | 0 -> Some Not_found | 1 -> Some Invalid_uri | 2 -> Some Malformed_rdf | 3 -> Some Unsupported_format | 4 -> Some Conflicting_triples | _ -> None -(* --- C FFI declarations --- *) - -external c_semweb_abi_version : unit -> int = "semweb_abi_version" -external c_semweb_create_context : unit -> int = "semweb_create_context" -external c_semweb_destroy_context : int -> unit = "semweb_destroy_context" -external c_semweb_can_transition : int -> int -> int = "semweb_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_semweb_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_semweb_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_semweb_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_semweb_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_siem.ml b/bindings/ocaml/lib/proven_siem.ml index d88eb607..ed34de88 100644 --- a/bindings/ocaml/lib/proven_siem.ml +++ b/bindings/ocaml/lib/proven_siem.ml @@ -64,12 +64,18 @@ let alert_state_of_tag = function | 0 -> Some New | 1 -> Some Acknowledged | 2 -> Some In_progress | 3 -> Some Resolved | 4 -> Some False_positive | _ -> None -(* --- C FFI declarations --- *) - -external c_siem_abi_version : unit -> int = "siem_abi_version" -external c_siem_create_context : unit -> int = "siem_create_context" -external c_siem_destroy_context : int -> unit = "siem_destroy_context" -external c_siem_can_transition : int -> int -> int = "siem_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_siem_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_siem_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_siem_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_siem_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_smb.ml b/bindings/ocaml/lib/proven_smb.ml index 664e28c2..2a1b772e 100644 --- a/bindings/ocaml/lib/proven_smb.ml +++ b/bindings/ocaml/lib/proven_smb.ml @@ -71,12 +71,18 @@ let session_state_of_tag = function | 3 -> Some Tree_connected | 4 -> Some File_open | 5 -> Some Disconnecting | _ -> None -(* --- C FFI declarations --- *) - -external c_smb_abi_version : unit -> int = "smb_abi_version" -external c_smb_create_context : unit -> int = "smb_create_context" -external c_smb_destroy_context : int -> unit = "smb_destroy_context" -external c_smb_can_transition : int -> int -> int = "smb_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_smb_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_smb_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_smb_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smb_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_smtp.ml b/bindings/ocaml/lib/proven_smtp.ml index 002c4ac1..a2a40a46 100644 --- a/bindings/ocaml/lib/proven_smtp.ml +++ b/bindings/ocaml/lib/proven_smtp.ml @@ -43,30 +43,54 @@ let auth_mech_of_tag = function | 0 -> Some Plain | 1 -> Some Login | 2 -> Some Cram_md5 | 3 -> Some Xoauth2 | _ -> None -(* --- C FFI declarations --- *) - -external c_smtp_abi_version : unit -> int = "smtp_abi_version" -external c_smtp_create_context : unit -> int = "smtp_create_context" -external c_smtp_destroy_context : int -> unit = "smtp_destroy_context" -external c_smtp_get_state : int -> int = "smtp_get_state" -external c_smtp_get_reply_code : int -> int = "smtp_get_reply_code" -external c_smtp_get_recipient_count : int -> int = "smtp_get_recipient_count" -external c_smtp_get_data_size : int -> int = "smtp_get_data_size" -external c_smtp_get_auth_mechanism : int -> int = "smtp_get_auth_mechanism" -external c_smtp_is_authenticated : int -> int = "smtp_is_authenticated" -external c_smtp_is_tls_active : int -> int = "smtp_is_tls_active" -external c_smtp_greet : int -> int -> int = "smtp_greet" -external c_smtp_authenticate : int -> int -> int = "smtp_authenticate" -external c_smtp_auth_complete : int -> int -> int = "smtp_auth_complete" -external c_smtp_set_sender : int -> int = "smtp_set_sender" -external c_smtp_add_recipient : int -> int = "smtp_add_recipient" -external c_smtp_start_data : int -> int = "smtp_start_data" -external c_smtp_append_data : int -> int -> int = "smtp_append_data" -external c_smtp_finish_data : int -> int = "smtp_finish_data" -external c_smtp_reset : int -> int = "smtp_reset" -external c_smtp_quit : int -> int = "smtp_quit" -external c_smtp_enable_tls : int -> int = "smtp_enable_tls" -external c_smtp_can_transition : int -> int -> int = "smtp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_smtp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_smtp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_smtp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_get_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_get_reply_code : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_get_recipient_count : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_get_data_size : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_get_auth_mechanism : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_is_authenticated : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_is_tls_active : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_greet : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_smtp_authenticate : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_smtp_auth_complete : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_smtp_set_sender : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_add_recipient : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_start_data : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_append_data : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_smtp_finish_data : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_reset : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_quit : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_enable_tls : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_smtp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_snmp.ml b/bindings/ocaml/lib/proven_snmp.ml index abae89b0..93e2f181 100644 --- a/bindings/ocaml/lib/proven_snmp.ml +++ b/bindings/ocaml/lib/proven_snmp.ml @@ -62,12 +62,18 @@ let error_status_of_tag = function | 13 -> Some Commit_failed | 14 -> Some Undo_failed | 15 -> Some Authorization_error | _ -> None -(* --- C FFI declarations --- *) - -external c_snmp_abi_version : unit -> int = "snmp_abi_version" -external c_snmp_create_context : unit -> int = "snmp_create_context" -external c_snmp_destroy_context : int -> unit = "snmp_destroy_context" -external c_snmp_can_transition : int -> int -> int = "snmp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_snmp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_snmp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_snmp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_snmp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_socks.ml b/bindings/ocaml/lib/proven_socks.ml index 3b741c94..39ebecf2 100644 --- a/bindings/ocaml/lib/proven_socks.ml +++ b/bindings/ocaml/lib/proven_socks.ml @@ -78,12 +78,18 @@ let state_of_tag = function | 0 -> Some Initial | 1 -> Some Authenticating | 2 -> Some Authenticated | 3 -> Some Connecting | 4 -> Some Established | 5 -> Some Closed | _ -> None -(* --- C FFI declarations --- *) - -external c_socks_abi_version : unit -> int = "socks_abi_version" -external c_socks_create_context : unit -> int = "socks_create_context" -external c_socks_destroy_context : int -> unit = "socks_destroy_context" -external c_socks_can_transition : int -> int -> int = "socks_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_socks_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_socks_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_socks_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_socks_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_sparql.ml b/bindings/ocaml/lib/proven_sparql.ml index 81ecb652..aee198eb 100644 --- a/bindings/ocaml/lib/proven_sparql.ml +++ b/bindings/ocaml/lib/proven_sparql.ml @@ -61,12 +61,18 @@ let sparql_error_type_of_tag = function | 2 -> Some Results_too_large | 3 -> Some Unknown_graph | 4 -> Some Access_denied | _ -> None -(* --- C FFI declarations --- *) - -external c_sparql_abi_version : unit -> int = "sparql_abi_version" -external c_sparql_create_context : unit -> int = "sparql_create_context" -external c_sparql_destroy_context : int -> unit = "sparql_destroy_context" -external c_sparql_can_transition : int -> int -> int = "sparql_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_sparql_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_sparql_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_sparql_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_sparql_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ssh.ml b/bindings/ocaml/lib/proven_ssh.ml index cadd9348..14c2c2f7 100644 --- a/bindings/ocaml/lib/proven_ssh.ml +++ b/bindings/ocaml/lib/proven_ssh.ml @@ -168,12 +168,18 @@ let channel_open_failure_of_tag = function | 0 -> Some Admin_prohibited | 1 -> Some Connect_failed | 2 -> Some Unknown_channel_type | 3 -> Some Resource_shortage | _ -> None -(* --- C FFI declarations --- *) - -external c_ssh_abi_version : unit -> int = "ssh_abi_version" -external c_ssh_create_context : unit -> int = "ssh_create_context" -external c_ssh_destroy_context : int -> unit = "ssh_destroy_context" -external c_ssh_can_transition : int -> int -> int = "ssh_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ssh_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ssh_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ssh_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_ssh_bastion.ml b/bindings/ocaml/lib/proven_ssh_bastion.ml index 32c3c781..b4d95ced 100644 --- a/bindings/ocaml/lib/proven_ssh_bastion.ml +++ b/bindings/ocaml/lib/proven_ssh_bastion.ml @@ -79,32 +79,58 @@ let disconnect_reason_of_tag = function | 4 -> Some Service_not_available | 5 -> Some By_application | 6 -> Some Too_many_connections | _ -> None -(* --- C FFI declarations --- *) - -external c_ssh_bastion_abi_version : unit -> int = "ssh_bastion_abi_version" -external c_ssh_bastion_create : int -> int -> int = "ssh_bastion_create" -external c_ssh_bastion_destroy : int -> unit = "ssh_bastion_destroy" -external c_ssh_bastion_state : int -> int = "ssh_bastion_state" -external c_ssh_bastion_kex_method : int -> int = "ssh_bastion_kex_method" -external c_ssh_bastion_auth_method : int -> int = "ssh_bastion_auth_method" -external c_ssh_bastion_can_transfer : int -> int = "ssh_bastion_can_transfer" -external c_ssh_bastion_disconnect_reason : int -> int = "ssh_bastion_disconnect_reason" -external c_ssh_bastion_auth_failures : int -> int = "ssh_bastion_auth_failures" -external c_ssh_bastion_complete_kex : int -> int = "ssh_bastion_complete_kex" -external c_ssh_bastion_authenticate : int -> int -> int = "ssh_bastion_authenticate" -external c_ssh_bastion_record_auth_failure : int -> int = "ssh_bastion_record_auth_failure" -external c_ssh_bastion_open_channel : int -> int -> int = "ssh_bastion_open_channel" -external c_ssh_bastion_confirm_channel : int -> int -> int = "ssh_bastion_confirm_channel" -external c_ssh_bastion_close_channel : int -> int -> int = "ssh_bastion_close_channel" -external c_ssh_bastion_channel_state : int -> int -> int = "ssh_bastion_channel_state" -external c_ssh_bastion_channel_type : int -> int -> int = "ssh_bastion_channel_type" -external c_ssh_bastion_channel_count : int -> int = "ssh_bastion_channel_count" -external c_ssh_bastion_rekey : int -> int = "ssh_bastion_rekey" -external c_ssh_bastion_disconnect : int -> int -> int = "ssh_bastion_disconnect" -external c_ssh_bastion_can_transition : int -> int -> int = "ssh_bastion_can_transition" -external c_ssh_bastion_audit_count : int -> int = "ssh_bastion_audit_count" -external c_ssh_bastion_set_recording : int -> int -> int = "ssh_bastion_set_recording" -external c_ssh_bastion_is_recording : int -> int = "ssh_bastion_is_recording" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_ssh_bastion_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_create : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_destroy : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_kex_method : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_auth_method : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_can_transfer : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_disconnect_reason : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_auth_failures : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_complete_kex : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_authenticate : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_record_auth_failure : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_open_channel : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_confirm_channel : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_close_channel : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_channel_state : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_channel_type : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_channel_count : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_rekey : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_disconnect : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_audit_count : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_set_recording : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_ssh_bastion_is_recording : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_stun.ml b/bindings/ocaml/lib/proven_stun.ml index 12669bd0..a76f9ff7 100644 --- a/bindings/ocaml/lib/proven_stun.ml +++ b/bindings/ocaml/lib/proven_stun.ml @@ -60,12 +60,18 @@ let error_code_of_tag = function | 3 -> Some Forbidden | 4 -> Some Mobility_forbidden | 5 -> Some Stale_nonce | 6 -> Some Server_error | 7 -> Some Insufficient_capacity | _ -> None -(* --- C FFI declarations --- *) - -external c_stun_abi_version : unit -> int = "stun_abi_version" -external c_stun_create_context : unit -> int = "stun_create_context" -external c_stun_destroy_context : int -> unit = "stun_destroy_context" -external c_stun_can_transition : int -> int -> int = "stun_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_stun_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_stun_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_stun_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_stun_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_syslog.ml b/bindings/ocaml/lib/proven_syslog.ml index 76ba0be7..a5bf3c0f 100644 --- a/bindings/ocaml/lib/proven_syslog.ml +++ b/bindings/ocaml/lib/proven_syslog.ml @@ -60,12 +60,18 @@ let transport_to_tag = function let transport_of_tag = function | 0 -> Some Udp514 | 1 -> Some Tcp514 | 2 -> Some Tls6514 | _ -> None -(* --- C FFI declarations --- *) - -external c_syslog_abi_version : unit -> int = "syslog_abi_version" -external c_syslog_create_context : unit -> int = "syslog_create_context" -external c_syslog_destroy_context : int -> unit = "syslog_destroy_context" -external c_syslog_can_transition : int -> int -> int = "syslog_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_syslog_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_syslog_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_syslog_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_syslog_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_tacacs.ml b/bindings/ocaml/lib/proven_tacacs.ml index b8df6ebe..1d957de1 100644 --- a/bindings/ocaml/lib/proven_tacacs.ml +++ b/bindings/ocaml/lib/proven_tacacs.ml @@ -108,12 +108,18 @@ let session_state_of_tag = function | 0 -> Some Idle | 1 -> Some Authenticating | 2 -> Some Authorizing | 3 -> Some Active | 4 -> Some Closing | _ -> None -(* --- C FFI declarations --- *) - -external c_tacacs_abi_version : unit -> int = "tacacs_abi_version" -external c_tacacs_create_context : unit -> int = "tacacs_create_context" -external c_tacacs_destroy_context : int -> unit = "tacacs_destroy_context" -external c_tacacs_can_transition : int -> int -> int = "tacacs_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_tacacs_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_tacacs_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_tacacs_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tacacs_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_telnet.ml b/bindings/ocaml/lib/proven_telnet.ml index 77eed585..9d4b2508 100644 --- a/bindings/ocaml/lib/proven_telnet.ml +++ b/bindings/ocaml/lib/proven_telnet.ml @@ -73,12 +73,18 @@ let session_state_of_tag = function | 0 -> Some Idle | 1 -> Some Negotiating | 2 -> Some Session_active | 3 -> Some Subneg | 4 -> Some Closing | _ -> None -(* --- C FFI declarations --- *) - -external c_telnet_abi_version : unit -> int = "telnet_abi_version" -external c_telnet_create_context : unit -> int = "telnet_create_context" -external c_telnet_destroy_context : int -> unit = "telnet_destroy_context" -external c_telnet_can_transition : int -> int -> int = "telnet_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_telnet_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_telnet_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_telnet_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_telnet_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_tftp.ml b/bindings/ocaml/lib/proven_tftp.ml index d660f8d0..7c8a502b 100644 --- a/bindings/ocaml/lib/proven_tftp.ml +++ b/bindings/ocaml/lib/proven_tftp.ml @@ -61,12 +61,18 @@ let transfer_state_of_tag = function | 0 -> Some Idle | 1 -> Some Reading | 2 -> Some Writing | 3 -> Some In_error | 4 -> Some Complete | _ -> None -(* --- C FFI declarations --- *) - -external c_tftp_abi_version : unit -> int = "tftp_abi_version" -external c_tftp_create_context : unit -> int = "tftp_create_context" -external c_tftp_destroy_context : int -> unit = "tftp_destroy_context" -external c_tftp_can_transition : int -> int -> int = "tftp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_tftp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_tftp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_tftp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tftp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_tls.ml b/bindings/ocaml/lib/proven_tls.ml index d005db48..f9a50c76 100644 --- a/bindings/ocaml/lib/proven_tls.ml +++ b/bindings/ocaml/lib/proven_tls.ml @@ -53,20 +53,34 @@ let cipher_suite_of_tag = function | 2 -> Some Chacha20_poly1305_sha256 | 3 -> Some Aes_ccm_128_sha256 | _ -> None -(* --- C FFI declarations --- *) - -external c_tls_abi_version : unit -> int = "tls_abi_version" -external c_tls_create_context : int -> int -> int = "tls_create_context" -external c_tls_destroy_context : int -> unit = "tls_destroy_context" -external c_tls_state : int -> int = "tls_state" -external c_tls_version : int -> int = "tls_version" -external c_tls_cipher_suite : int -> int = "tls_cipher_suite" -external c_tls_is_handshake_complete : int -> int = "tls_is_handshake_complete" -external c_tls_begin_handshake : int -> int = "tls_begin_handshake" -external c_tls_complete_handshake : int -> int = "tls_complete_handshake" -external c_tls_renegotiate : int -> int = "tls_renegotiate" -external c_tls_shutdown : int -> int = "tls_shutdown" -external c_tls_can_transition : int -> int -> int = "tls_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_tls_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_tls_create_context : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () +let c_tls_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tls_state : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tls_version : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tls_cipher_suite : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tls_is_handshake_complete : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tls_begin_handshake : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tls_complete_handshake : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tls_renegotiate : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tls_shutdown : int -> int = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_tls_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_triplestore.ml b/bindings/ocaml/lib/proven_triplestore.ml index 82e11011..c1a7fd5f 100644 --- a/bindings/ocaml/lib/proven_triplestore.ml +++ b/bindings/ocaml/lib/proven_triplestore.ml @@ -82,12 +82,18 @@ let store_state_of_tag = function | 0 -> Some Idle | 1 -> Some Ready | 2 -> Some In_transaction | 3 -> Some Importing | 4 -> Some Closing | _ -> None -(* --- C FFI declarations --- *) - -external c_triplestore_abi_version : unit -> int = "triplestore_abi_version" -external c_triplestore_create_context : unit -> int = "triplestore_create_context" -external c_triplestore_destroy_context : int -> unit = "triplestore_destroy_context" -external c_triplestore_can_transition : int -> int -> int = "triplestore_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_triplestore_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_triplestore_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_triplestore_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_triplestore_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_unavailable.ml b/bindings/ocaml/lib/proven_unavailable.ml new file mode 100644 index 00000000..a704b2de --- /dev/null +++ b/bindings/ocaml/lib/proven_unavailable.ml @@ -0,0 +1,8 @@ +(* SPDX-License-Identifier: MPL-2.0 *) +(* Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) *) + +(** Shared fail-closed error for OCaml operations whose native C stubs have + not been implemented. Raw Zig C exports are not OCaml runtime primitives. *) +let raise_unavailable () = + failwith + "proven-servers OCaml FFI is unavailable: OCaml-compatible C stubs are not implemented" diff --git a/bindings/ocaml/lib/proven_virt.ml b/bindings/ocaml/lib/proven_virt.ml index 7855451c..d16adc82 100644 --- a/bindings/ocaml/lib/proven_virt.ml +++ b/bindings/ocaml/lib/proven_virt.ml @@ -78,12 +78,18 @@ let boot_device_of_tag = function | 0 -> Some Hard_disk | 1 -> Some Cdrom | 2 -> Some Network | 3 -> Some Usb | _ -> None -(* --- C FFI declarations --- *) - -external c_virt_abi_version : unit -> int = "virt_abi_version" -external c_virt_create_context : unit -> int = "virt_create_context" -external c_virt_destroy_context : int -> unit = "virt_destroy_context" -external c_virt_can_transition : int -> int -> int = "virt_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_virt_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_virt_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_virt_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_virt_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_voip.ml b/bindings/ocaml/lib/proven_voip.ml index 33c93b6b..e850cb13 100644 --- a/bindings/ocaml/lib/proven_voip.ml +++ b/bindings/ocaml/lib/proven_voip.ml @@ -64,12 +64,18 @@ let dialog_state_to_tag = function let dialog_state_of_tag = function | 0 -> Some Early | 1 -> Some Confirmed | 2 -> Some Terminated | _ -> None -(* --- C FFI declarations --- *) - -external c_voip_abi_version : unit -> int = "voip_abi_version" -external c_voip_create_context : unit -> int = "voip_create_context" -external c_voip_destroy_context : int -> unit = "voip_destroy_context" -external c_voip_can_transition : int -> int -> int = "voip_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_voip_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_voip_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_voip_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_voip_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_vpn.ml b/bindings/ocaml/lib/proven_vpn.ml index f249ec4d..756ae60a 100644 --- a/bindings/ocaml/lib/proven_vpn.ml +++ b/bindings/ocaml/lib/proven_vpn.ml @@ -120,12 +120,18 @@ let vpn_error_of_tag = function | 2 -> Some Lifetime_expired | 3 -> Some Invalid_spi | 4 -> Some Replay_detected | 5 -> Some Negotiation_timeout | _ -> None -(* --- C FFI declarations --- *) - -external c_vpn_abi_version : unit -> int = "vpn_abi_version" -external c_vpn_create_context : unit -> int = "vpn_create_context" -external c_vpn_destroy_context : int -> unit = "vpn_destroy_context" -external c_vpn_can_transition : int -> int -> int = "vpn_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_vpn_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_vpn_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_vpn_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_vpn_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_wasm.ml b/bindings/ocaml/lib/proven_wasm.ml index cdf9e329..2e83566b 100644 --- a/bindings/ocaml/lib/proven_wasm.ml +++ b/bindings/ocaml/lib/proven_wasm.ml @@ -44,12 +44,18 @@ let mutability_to_tag = function let mutability_of_tag = function | 0 -> Some Immutable | 1 -> Some Mutable | _ -> None -(* --- C FFI declarations --- *) - -external c_wasm_abi_version : unit -> int = "wasm_abi_version" -external c_wasm_create_context : unit -> int = "wasm_create_context" -external c_wasm_destroy_context : int -> unit = "wasm_destroy_context" -external c_wasm_can_transition : int -> int -> int = "wasm_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_wasm_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_wasm_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_wasm_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_wasm_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_webdav.ml b/bindings/ocaml/lib/proven_webdav.ml index abbff9e0..d92e4b81 100644 --- a/bindings/ocaml/lib/proven_webdav.ml +++ b/bindings/ocaml/lib/proven_webdav.ml @@ -82,12 +82,18 @@ let property_op_to_tag = function let property_op_of_tag = function | 0 -> Some Set | 1 -> Some Remove | _ -> None -(* --- C FFI declarations --- *) - -external c_webdav_abi_version : unit -> int = "webdav_abi_version" -external c_webdav_create_context : unit -> int = "webdav_create_context" -external c_webdav_destroy_context : int -> unit = "webdav_destroy_context" -external c_webdav_can_transition : int -> int -> int = "webdav_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_webdav_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_webdav_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_webdav_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_webdav_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_websocket.ml b/bindings/ocaml/lib/proven_websocket.ml index 39e3fdea..2a42108d 100644 --- a/bindings/ocaml/lib/proven_websocket.ml +++ b/bindings/ocaml/lib/proven_websocket.ml @@ -42,12 +42,18 @@ let close_code_of_tag = function | 8 -> Some Message_too_big | 9 -> Some Mandatory_extension | 10 -> Some Internal_error | _ -> None -(* --- C FFI declarations --- *) +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) -external c_websocket_abi_version : unit -> int = "websocket_abi_version" -external c_websocket_create_context : unit -> int = "websocket_create_context" -external c_websocket_destroy_context : int -> unit = "websocket_destroy_context" -external c_websocket_can_transition : int -> int -> int = "websocket_can_transition" +let c_websocket_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_websocket_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_websocket_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_websocket_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_xmpp.ml b/bindings/ocaml/lib/proven_xmpp.ml index 00f1e8e7..e888d201 100644 --- a/bindings/ocaml/lib/proven_xmpp.ml +++ b/bindings/ocaml/lib/proven_xmpp.ml @@ -74,12 +74,18 @@ let stream_error_of_tag = function | 6 -> Some Policy_violation | 7 -> Some Resource_constraint | 8 -> Some System_shutdown | _ -> None -(* --- C FFI declarations --- *) - -external c_xmpp_abi_version : unit -> int = "xmpp_abi_version" -external c_xmpp_create_context : unit -> int = "xmpp_create_context" -external c_xmpp_destroy_context : int -> unit = "xmpp_destroy_context" -external c_xmpp_can_transition : int -> int -> int = "xmpp_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_xmpp_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_xmpp_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_xmpp_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_xmpp_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/ocaml/lib/proven_zerotrust.ml b/bindings/ocaml/lib/proven_zerotrust.ml index 3f3eaed0..17845af5 100644 --- a/bindings/ocaml/lib/proven_zerotrust.ml +++ b/bindings/ocaml/lib/proven_zerotrust.ml @@ -89,12 +89,18 @@ let auth_factor_of_tag = function | 0 -> Some Certificate | 1 -> Some Token | 2 -> Some Biometric | 3 -> Some Fido2 | 4 -> Some Totp | 5 -> Some Push | _ -> None -(* --- C FFI declarations --- *) - -external c_zerotrust_abi_version : unit -> int = "zerotrust_abi_version" -external c_zerotrust_create_context : unit -> int = "zerotrust_create_context" -external c_zerotrust_destroy_context : int -> unit = "zerotrust_destroy_context" -external c_zerotrust_can_transition : int -> int -> int = "zerotrust_can_transition" +(* --- Disabled native FFI declarations --- *) +(* Raw Zig C symbols are not OCaml primitives. All operations + raise a clear exception until OCaml-compatible stubs exist. *) + +let c_zerotrust_abi_version : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_zerotrust_create_context : unit -> int = fun () -> + Proven_unavailable.raise_unavailable () +let c_zerotrust_destroy_context : int -> unit = fun _arg0 -> + Proven_unavailable.raise_unavailable () +let c_zerotrust_can_transition : int -> int -> int = fun _arg0 _arg1 -> + Proven_unavailable.raise_unavailable () (* --- Safe wrappers --- *) diff --git a/bindings/php/src/ProvenDns.php b/bindings/php/src/ProvenDns.php index 95dbf9b6..ccb496f5 100644 --- a/bindings/php/src/ProvenDns.php +++ b/bindings/php/src/ProvenDns.php @@ -3,10 +3,10 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) // -// PHP bindings for the proven-dns Zig FFI. -// -// Wraps the C-ABI functions for DNS query/response lifecycle, -// DNSSEC signing and validation, and record management. +// PHP bindings for the bounded proven-dns message-builder FFI. +// Accepts only exact 17-byte standard queries with one root-name question; +// responses are capped at 512 bytes. This is not a general resolver. DNSSEC +// key loading, signing, and validation fail closed. declare(strict_types=1); @@ -22,7 +22,7 @@ enum DnsState: int case Sent = 4; } -/** DNSSEC states matching Idris2 ABI tags. */ +/** DNSSEC state tags; the cryptographic transitions are unavailable in the FFI. */ enum DnssecState: int { case Disabled = 0; @@ -42,7 +42,8 @@ enum DnssecAlgorithm: int } /** - * DNS query/response context wrapping a Zig FFI slot. + * Bounded DNS message-builder context wrapping a Zig FFI slot. + * Only the exact 17-byte root-question subset is accepted; output is at most 512 bytes. */ final class ProvenDns { @@ -52,17 +53,17 @@ final class ProvenDns uint8_t dns_state(int slot); uint8_t dns_dnssec_state(int slot); uint8_t dns_rcode(int slot); - uint32_t dns_answer_count(int slot); - uint32_t dns_authority_count(int slot); - uint32_t dns_additional_count(int slot); - uint16_t dns_query_rtype(int slot); - uint16_t dns_query_class(int slot); - uint8_t dns_parse_query(int slot, const uint8_t *data, uint32_t len); + uint16_t dns_answer_count(int slot); + uint16_t dns_authority_count(int slot); + uint16_t dns_additional_count(int slot); + uint8_t dns_query_rtype(int slot); + uint8_t dns_query_class(int slot); + uint8_t dns_parse_query(int slot, const uint8_t *data, uint16_t len); uint8_t dns_begin_lookup(int slot); uint8_t dns_begin_response(int slot); - uint8_t dns_add_answer(int slot, uint16_t rtype, uint16_t rclass, uint32_t ttl, const uint8_t *rdata, uint32_t rdata_len); - uint8_t dns_add_authority(int slot, uint16_t rtype, uint16_t rclass, uint32_t ttl, const uint8_t *rdata, uint32_t rdata_len); - uint8_t dns_add_additional(int slot, uint16_t rtype, uint16_t rclass, uint32_t ttl, const uint8_t *rdata, uint32_t rdata_len); + uint8_t dns_add_answer(int slot, uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t *rdata, uint16_t rdata_len); + uint8_t dns_add_authority(int slot, uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t *rdata, uint16_t rdata_len); + uint8_t dns_add_additional(int slot, uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t *rdata, uint16_t rdata_len); uint8_t dns_set_rcode(int slot, uint8_t rcode); uint8_t dns_build_response(int slot, uint8_t *buf, uint16_t *out_len); uint8_t dns_enable_dnssec(int slot); @@ -127,6 +128,9 @@ public function queryClass(): int { return self::ffi()->dns_query_class($this->s /** @throws ProvenError */ public function parseQuery(string $data): void { + if (strlen($data) !== 17) { + throw new \InvalidArgumentException("DNS query must be exactly 17 bytes"); + } ProvenError::checkStatus(self::ffi()->dns_parse_query($this->slot, $data, strlen($data))); } @@ -138,18 +142,27 @@ public function beginResponse(): void { ProvenError::checkStatus(self::ffi()->dn /** @throws ProvenError */ public function addAnswer(int $rtype, int $rclass, int $ttl, string $rdata): void { + if (strlen($rdata) > 256) { + throw new \InvalidArgumentException("DNS RDATA must not exceed 256 bytes"); + } ProvenError::checkStatus(self::ffi()->dns_add_answer($this->slot, $rtype, $rclass, $ttl, $rdata, strlen($rdata))); } /** @throws ProvenError */ public function addAuthority(int $rtype, int $rclass, int $ttl, string $rdata): void { + if (strlen($rdata) > 256) { + throw new \InvalidArgumentException("DNS RDATA must not exceed 256 bytes"); + } ProvenError::checkStatus(self::ffi()->dns_add_authority($this->slot, $rtype, $rclass, $ttl, $rdata, strlen($rdata))); } /** @throws ProvenError */ public function addAdditional(int $rtype, int $rclass, int $ttl, string $rdata): void { + if (strlen($rdata) > 256) { + throw new \InvalidArgumentException("DNS RDATA must not exceed 256 bytes"); + } ProvenError::checkStatus(self::ffi()->dns_add_additional($this->slot, $rtype, $rclass, $ttl, $rdata, strlen($rdata))); } @@ -160,32 +173,36 @@ public function setRcode(int $rcodeTag): void } /** - * Build the DNS response wire format. + * Build a response for the minimal root-question model; output is capped at 512 bytes. * - * @param int $maxLen Maximum response length. + * @param int $maxLen Output buffer capacity; must be at least 512 bytes. * @return string Serialized DNS response bytes. * @throws ProvenError */ public function buildResponse(int $maxLen = 512): string { + if ($maxLen < 512) { + throw new \InvalidArgumentException('DNS response buffer must be at least 512 bytes'); + } $buf = \FFI::new("uint8_t[{$maxLen}]"); $outLen = \FFI::new('uint16_t'); ProvenError::checkStatus(self::ffi()->dns_build_response($this->slot, $buf, \FFI::addr($outLen))); return \FFI::string($buf, $outLen->cdata); } - /** @throws ProvenError */ + /** Enable mode only; response construction then rejects without a signer. @throws ProvenError */ public function enableDnssec(): void { ProvenError::checkStatus(self::ffi()->dns_enable_dnssec($this->slot)); } - /** @throws ProvenError */ + /** Always fails closed: the ABI accepts no private-key material. @throws ProvenError */ public function loadDnssecKey(DnssecAlgorithm $algo): void { ProvenError::checkStatus(self::ffi()->dns_load_dnssec_key($this->slot, $algo->value)); } - /** @throws ProvenError */ + /** Always fails closed because no DNSSEC signing backend exists. @throws ProvenError */ public function signResponse(): void { ProvenError::checkStatus(self::ffi()->dns_sign_response($this->slot)); } + /** Always returns false because no DNSSEC validator exists. */ public function validateDnssec(): bool { return self::ffi()->dns_validate_dnssec($this->slot) === 0; } public static function abiVersion(): int { return self::ffi()->dns_abi_version(); } diff --git a/bindings/python/proven_servers/dns.py b/bindings/python/proven_servers/dns.py index b88cfcd7..d8c59d02 100644 --- a/bindings/python/proven_servers/dns.py +++ b/bindings/python/proven_servers/dns.py @@ -1,7 +1,10 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) Jonathan D.A. Jewell # -# Python bindings for the proven-dns Zig FFI. +# Python bindings for the bounded proven-dns message-builder FFI. +# Accepts only exact 17-byte standard queries with one root-name question; +# responses are capped at 512 bytes. It is not a general resolver, and DNSSEC +# key loading, signing, and validation fail closed. # # Wraps the C-ABI functions from protocols/proven-dns/ffi/zig/src/dns.zig: # - Context lifecycle: dns_create_context, dns_destroy_context @@ -40,7 +43,7 @@ class DnsState(IntEnum): class DnssecState(IntEnum): - """DNSSEC states matching the Idris2 ABI tags.""" + """DNSSEC ABI/model tags; key loading/signing/validation are unavailable.""" DISABLED = 0 ENABLED = 1 KEY_LOADED = 2 @@ -217,7 +220,7 @@ def query_class(self) -> int: # -- Lifecycle --------------------------------------------------------- def parse_query(self, data: bytes) -> None: - """Parse a DNS query from raw bytes. Transitions Idle -> QueryReceived. + """Parse the exact 17-byte standard root-question query subset. Args: data: Raw DNS query bytes. @@ -225,6 +228,8 @@ def parse_query(self, data: bytes) -> None: Raises: ProvenError: On invalid state or malformed query. """ + if len(data) != 17: + raise ValueError("DNS query must be exactly 17 bytes") buf = (ctypes.c_uint8 * len(data))(*data) check_status(self._lib.dns_parse_query(self._slot, buf, len(data))) @@ -247,16 +252,22 @@ def add_answer(self, rtype: int, rclass: int, ttl: int, rdata: bytes) -> None: ttl: Time-to-live in seconds. rdata: Raw record data bytes. """ + if len(rdata) > 256: + raise ValueError("DNS RDATA must not exceed 256 bytes") buf = (ctypes.c_uint8 * len(rdata))(*rdata) check_status(self._lib.dns_add_answer(self._slot, rtype, rclass, ttl, buf, len(rdata))) def add_authority(self, rtype: int, rclass: int, ttl: int, rdata: bytes) -> None: """Add a resource record to the authority section.""" + if len(rdata) > 256: + raise ValueError("DNS RDATA must not exceed 256 bytes") buf = (ctypes.c_uint8 * len(rdata))(*rdata) check_status(self._lib.dns_add_authority(self._slot, rtype, rclass, ttl, buf, len(rdata))) def add_additional(self, rtype: int, rclass: int, ttl: int, rdata: bytes) -> None: """Add a resource record to the additional section.""" + if len(rdata) > 256: + raise ValueError("DNS RDATA must not exceed 256 bytes") buf = (ctypes.c_uint8 * len(rdata))(*rdata) check_status(self._lib.dns_add_additional(self._slot, rtype, rclass, ttl, buf, len(rdata))) @@ -267,14 +278,13 @@ def set_rcode(self, rcode_tag: int) -> None: check_status(self._lib.dns_set_rcode(self._slot, rcode_tag)) def build_response(self, max_len: int = 512) -> bytes: - """Build the DNS response message. Transitions ResponseBuilding -> Sent. + """Build the bounded response. Requires a buffer of at least 512 bytes. - Args: - max_len: Maximum response buffer size (default 512). - - Returns: - The serialized DNS response bytes. + The FFI supports only a root-name question, caps responses at 512 bytes, + and rejects DNSSEC-enabled contexts because signing is unavailable. """ + if max_len < 512: + raise ValueError("DNS response buffer must be at least 512 bytes") buf = (ctypes.c_uint8 * max_len)() out_len = ctypes.c_uint16(0) check_status(self._lib.dns_build_response(self._slot, buf, ctypes.byref(out_len))) @@ -283,19 +293,19 @@ def build_response(self, max_len: int = 512) -> bytes: # -- DNSSEC ------------------------------------------------------------ def enable_dnssec(self) -> None: - """Enable DNSSEC. Transitions Disabled -> Enabled.""" + """Enable mode only; response building then rejects because no signer exists.""" check_status(self._lib.dns_enable_dnssec(self._slot)) def load_dnssec_key(self, algo: DnssecAlgorithm) -> None: - """Load a DNSSEC signing key. Transitions Enabled -> KeyLoaded.""" + """Always fails closed: the ABI accepts no private-key material.""" check_status(self._lib.dns_load_dnssec_key(self._slot, algo.value)) def sign_response(self) -> None: - """Sign the response (DNSSEC). Transitions KeyLoaded -> Validated.""" + """Always fails closed because no DNSSEC signing backend exists.""" check_status(self._lib.dns_sign_response(self._slot)) def validate_dnssec(self) -> bool: - """Check DNSSEC validation result. Returns True if validated.""" + """Always returns False because no DNSSEC validator exists.""" return self._lib.dns_validate_dnssec(self._slot) == 0 diff --git a/bindings/ruby/lib/proven_servers/dns.rb b/bindings/ruby/lib/proven_servers/dns.rb index 2e3a7f19..8d3722be 100644 --- a/bindings/ruby/lib/proven_servers/dns.rb +++ b/bindings/ruby/lib/proven_servers/dns.rb @@ -1,17 +1,17 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# Ruby bindings for the proven-dns Zig FFI. -# -# Wraps the C-ABI functions for DNS query/response lifecycle, -# DNSSEC signing and validation, and record management. +# Ruby bindings for the bounded proven-dns message-builder FFI. +# Accepts only exact 17-byte standard queries with one root-name question; +# responses are capped at 512 bytes. This is not a general resolver. DNSSEC +# key loading, signing, and validation fail closed. # frozen_string_literal: true require "ffi" module ProvenServers - # DNS server protocol bindings matching the Idris2 ABI. + # Bounded DNS message-builder bindings matching the Idris2 ABI. # # @example # ProvenServers::Dns.with_context do |ctx| @@ -58,17 +58,17 @@ module DnssecAlgorithm attach_function :dns_state, [:int], :uint8 attach_function :dns_dnssec_state, [:int], :uint8 attach_function :dns_rcode, [:int], :uint8 - attach_function :dns_answer_count, [:int], :uint32 - attach_function :dns_authority_count, [:int], :uint32 - attach_function :dns_additional_count, [:int], :uint32 - attach_function :dns_query_rtype, [:int], :uint16 - attach_function :dns_query_class, [:int], :uint16 - attach_function :dns_parse_query, [:int, :pointer, :uint32], :uint8 + attach_function :dns_answer_count, [:int], :uint16 + attach_function :dns_authority_count, [:int], :uint16 + attach_function :dns_additional_count,[:int], :uint16 + attach_function :dns_query_rtype, [:int], :uint8 + attach_function :dns_query_class, [:int], :uint8 + attach_function :dns_parse_query, [:int, :pointer, :uint16], :uint8 attach_function :dns_begin_lookup, [:int], :uint8 attach_function :dns_begin_response, [:int], :uint8 - attach_function :dns_add_answer, [:int, :uint16, :uint16, :uint32, :pointer, :uint32], :uint8 - attach_function :dns_add_authority, [:int, :uint16, :uint16, :uint32, :pointer, :uint32], :uint8 - attach_function :dns_add_additional, [:int, :uint16, :uint16, :uint32, :pointer, :uint32], :uint8 + attach_function :dns_add_answer, [:int, :uint8, :uint8, :uint32, :pointer, :uint16], :uint8 + attach_function :dns_add_authority, [:int, :uint8, :uint8, :uint32, :pointer, :uint16], :uint8 + attach_function :dns_add_additional, [:int, :uint8, :uint8, :uint32, :pointer, :uint16], :uint8 attach_function :dns_set_rcode, [:int, :uint8], :uint8 attach_function :dns_build_response, [:int, :pointer, :pointer], :uint8 attach_function :dns_enable_dnssec, [:int], :uint8 @@ -132,12 +132,14 @@ def query_rtype = Dns.dns_query_rtype(@slot) # @return [Integer] def query_class = Dns.dns_query_class(@slot) - # Parse a raw DNS query. + # Parse only the exact 17-byte standard root-question query subset. # # @param data [String] raw DNS query bytes # @return [void] # @raise [ProvenError] on failure def parse_query(data) + raise ArgumentError, "DNS query must be exactly 17 bytes" unless data.bytesize == 17 + buf = FFI::MemoryPointer.from_string(data) ProvenServers.check_status(Dns.dns_parse_query(@slot, buf, data.bytesize)) end @@ -158,6 +160,8 @@ def begin_response = ProvenServers.check_status(Dns.dns_begin_response(@slot)) # @return [void] # @raise [ProvenError] on failure def add_answer(rtype, rclass, ttl, rdata) + raise ArgumentError, "DNS RDATA must not exceed 256 bytes" if rdata.bytesize > 256 + buf = FFI::MemoryPointer.from_string(rdata) ProvenServers.check_status( Dns.dns_add_answer(@slot, rtype, rclass, ttl, buf, rdata.bytesize) @@ -172,6 +176,8 @@ def add_answer(rtype, rclass, ttl, rdata) # @param rdata [String] record data bytes # @return [void] def add_authority(rtype, rclass, ttl, rdata) + raise ArgumentError, "DNS RDATA must not exceed 256 bytes" if rdata.bytesize > 256 + buf = FFI::MemoryPointer.from_string(rdata) ProvenServers.check_status( Dns.dns_add_authority(@slot, rtype, rclass, ttl, buf, rdata.bytesize) @@ -186,6 +192,8 @@ def add_authority(rtype, rclass, ttl, rdata) # @param rdata [String] record data bytes # @return [void] def add_additional(rtype, rclass, ttl, rdata) + raise ArgumentError, "DNS RDATA must not exceed 256 bytes" if rdata.bytesize > 256 + buf = FFI::MemoryPointer.from_string(rdata) ProvenServers.check_status( Dns.dns_add_additional(@slot, rtype, rclass, ttl, buf, rdata.bytesize) @@ -205,16 +213,19 @@ def set_rcode(rcode_tag) # @param max_len [Integer] maximum response length # @return [String] serialized DNS response bytes def build_response(max_len: 512) + raise ArgumentError, "DNS response buffer must be at least 512 bytes" if max_len < 512 + buf = FFI::MemoryPointer.new(:uint8, max_len) out_len = FFI::MemoryPointer.new(:uint16) ProvenServers.check_status(Dns.dns_build_response(@slot, buf, out_len)) buf.read_string(out_len.read_uint16) end + # Enable mode only; response construction then rejects without a signer. # @return [void] def enable_dnssec = ProvenServers.check_status(Dns.dns_enable_dnssec(@slot)) - # Load a DNSSEC signing key. + # Always fails closed: the ABI accepts no private-key material. # # @param algo [Integer] DnssecAlgorithm tag # @return [void] @@ -222,9 +233,11 @@ def load_dnssec_key(algo) ProvenServers.check_status(Dns.dns_load_dnssec_key(@slot, algo)) end + # Always fails closed because no DNSSEC signing backend exists. # @return [void] def sign_response = ProvenServers.check_status(Dns.dns_sign_response(@slot)) + # Always returns false because no DNSSEC validator exists. # @return [Boolean] def validate_dnssec? = Dns.dns_validate_dnssec(@slot) == 0 end diff --git a/bindings/rust/src/ffi_dns.rs b/bindings/rust/src/ffi_dns.rs index dcda6b11..3afcf73f 100644 --- a/bindings/rust/src/ffi_dns.rs +++ b/bindings/rust/src/ffi_dns.rs @@ -1,7 +1,12 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Jonathan D.A. Jewell // -//! Safe Rust wrappers around the `proven-dns` Zig FFI exports. +//! Safe Rust wrappers around the bounded `proven-dns` message-builder FFI. +//! +//! The parser accepts only an exact 17-byte standard query with one root-name +//! question and RD as the only supported flag. Responses are capped at 512 bytes. +//! This is not a general resolver; DNSSEC key loading, signing, and validation +//! fail closed (validation therefore always returns false). //! //! Wraps the C-ABI functions from `protocols/proven-dns/ffi/zig/src/dns.zig`: //! - Context lifecycle: `dns_create_context`, `dns_destroy_context` @@ -9,8 +14,8 @@ //! - Lifecycle transitions: `dns_begin_lookup`, `dns_begin_response` //! - Record management: `dns_add_answer`, `dns_add_authority`, `dns_add_additional` //! - Response building: `dns_set_rcode`, `dns_build_response` -//! - DNSSEC: `dns_enable_dnssec`, `dns_load_dnssec_key`, `dns_sign_response`, -//! `dns_validate_dnssec` +//! - DNSSEC configuration/model calls: `dns_enable_dnssec` succeeds, while +//! key loading, signing, and validation are unavailable and fail closed //! - State queries: `dns_state`, `dns_dnssec_state`, `dns_rcode`, //! `dns_answer_count`, `dns_authority_count`, `dns_additional_count`, //! `dns_query_rtype`, `dns_query_class` @@ -73,9 +78,9 @@ pub enum DnssecState { Disabled = 0, /// DNSSEC enabled, no key loaded. Enabled = 1, - /// DNSSEC key loaded. + /// ABI/model state only; operational key loading is unavailable. KeyLoaded = 2, - /// Response validated / signed. + /// Abstract ABI state; the current FFI cannot reach it through crypto. Validated = 3, } @@ -258,6 +263,9 @@ pub fn query_class(ctx: &DnsContext) -> u8 { /// Parse a DNS query from raw bytes. Transitions Idle -> QueryReceived. #[cfg(feature = "ffi")] pub fn parse_query(ctx: &DnsContext, data: &[u8]) -> ProvenResult<()> { + if data.len() != 17 { + return Err(ProvenError::InvalidParameter); + } let result = unsafe { dns_parse_query(ctx.slot, data.as_ptr(), data.len() as u16) }; @@ -283,6 +291,9 @@ pub fn begin_response(ctx: &DnsContext) -> ProvenResult<()> { /// Only valid in ResponseBuilding state. Record type and class are ABI tags. #[cfg(feature = "ffi")] pub fn add_answer(ctx: &DnsContext, rtype: u8, rclass: u8, ttl: u32, rdata: &[u8]) -> ProvenResult<()> { + if rdata.len() > 256 { + return Err(ProvenError::CapacityExceeded); + } let result = unsafe { dns_add_answer(ctx.slot, rtype, rclass, ttl, rdata.as_ptr(), rdata.len() as u16) }; @@ -292,6 +303,9 @@ pub fn add_answer(ctx: &DnsContext, rtype: u8, rclass: u8, ttl: u32, rdata: &[u8 /// Add a resource record to the authority section. #[cfg(feature = "ffi")] pub fn add_authority(ctx: &DnsContext, rtype: u8, rclass: u8, ttl: u32, rdata: &[u8]) -> ProvenResult<()> { + if rdata.len() > 256 { + return Err(ProvenError::CapacityExceeded); + } let result = unsafe { dns_add_authority(ctx.slot, rtype, rclass, ttl, rdata.as_ptr(), rdata.len() as u16) }; @@ -301,6 +315,9 @@ pub fn add_authority(ctx: &DnsContext, rtype: u8, rclass: u8, ttl: u32, rdata: & /// Add a resource record to the additional section. #[cfg(feature = "ffi")] pub fn add_additional(ctx: &DnsContext, rtype: u8, rclass: u8, ttl: u32, rdata: &[u8]) -> ProvenResult<()> { + if rdata.len() > 256 { + return Err(ProvenError::CapacityExceeded); + } let result = unsafe { dns_add_additional(ctx.slot, rtype, rclass, ttl, rdata.as_ptr(), rdata.len() as u16) }; @@ -314,12 +331,15 @@ pub fn set_rcode(ctx: &DnsContext, rcode_tag: u8) -> ProvenResult<()> { ProvenError::from_status(result) } -/// Build the DNS response message. Transitions ResponseBuilding -> Sent. +/// Build the bounded root-question DNS message. Transitions ResponseBuilding -> Sent. /// /// The output buffer must be at least 512 bytes. On success, returns the /// number of bytes written to `out`. #[cfg(feature = "ffi")] pub fn build_response(ctx: &DnsContext, out: &mut [u8]) -> ProvenResult { + if out.len() < 512 { + return Err(ProvenError::CapacityExceeded); + } let mut out_len: u16 = 0; let result = unsafe { dns_build_response(ctx.slot, out.as_mut_ptr(), &mut out_len) @@ -327,28 +347,28 @@ pub fn build_response(ctx: &DnsContext, out: &mut [u8]) -> ProvenResult { ProvenError::from_status(result).map(|()| out_len) } -/// Enable DNSSEC. Transitions Disabled -> Enabled. +/// Enable DNSSEC mode only; response construction then rejects because no signer exists. #[cfg(feature = "ffi")] pub fn enable_dnssec(ctx: &DnsContext) -> ProvenResult<()> { let result = unsafe { dns_enable_dnssec(ctx.slot) }; ProvenError::from_status(result) } -/// Load a DNSSEC signing key. Transitions Enabled -> KeyLoaded. +/// Attempt to load a DNSSEC key; always rejects because the ABI accepts no key material. #[cfg(feature = "ffi")] pub fn load_dnssec_key(ctx: &DnsContext, algo: DnssecAlgorithm) -> ProvenResult<()> { let result = unsafe { dns_load_dnssec_key(ctx.slot, algo.to_tag()) }; ProvenError::from_status(result) } -/// Sign the response (DNSSEC). Transitions KeyLoaded -> Validated. +/// Attempt DNSSEC signing; always rejects because no signing backend is present. #[cfg(feature = "ffi")] pub fn sign_response(ctx: &DnsContext) -> ProvenResult<()> { let result = unsafe { dns_sign_response(ctx.slot) }; ProvenError::from_status(result) } -/// Check DNSSEC validation result. Returns `true` if validated. +/// Check DNSSEC validation result; always returns `false` because no validator exists. #[cfg(feature = "ffi")] pub fn validate_dnssec(ctx: &DnsContext) -> bool { unsafe { dns_validate_dnssec(ctx.slot) == 0 } diff --git a/bindings/swift/Sources/ProvenServers/ProvenDns.swift b/bindings/swift/Sources/ProvenServers/ProvenDns.swift index d4eeccca..adad413b 100644 --- a/bindings/swift/Sources/ProvenServers/ProvenDns.swift +++ b/bindings/swift/Sources/ProvenServers/ProvenDns.swift @@ -1,8 +1,9 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) // -// Swift bindings for the proven-dns protocol. -// Wraps the C-ABI functions from protocols/proven-dns/ffi/zig/src/dns.zig. +// Swift bindings for the bounded proven-dns message-builder FFI. +// Only exact 17-byte standard queries with one root-name question are accepted; +// responses are capped at 512 bytes. DNSSEC cryptographic operations fail closed. // Enums match Idris2 ABI tags exactly (DnsABI.Layout). import Foundation @@ -35,6 +36,20 @@ import Foundation @_silgen_name("dns_can_transition") private func dns_can_transition(_ from: UInt8, _ to: UInt8) -> UInt8 @_silgen_name("dns_can_dnssec_transition") private func dns_can_dnssec_transition(_ from: UInt8, _ to: UInt8) -> UInt8 +// Pass a stable non-null pointer even for empty RDATA; the FFI ignores it when len is zero. +private func withDnsRdataPointer( + _ data: Data, + body: (UnsafePointer, UInt16) -> UInt8 +) -> UInt8 { + if data.isEmpty { + var emptyByte: UInt8 = 0 + return withUnsafePointer(to: &emptyByte) { body($0, 0) } + } + return data.withUnsafeBytes { buffer in + body(buffer.baseAddress!.assumingMemoryBound(to: UInt8.self), UInt16(buffer.count)) + } +} + // MARK: - Enums matching Idris2 ABI tags /// DNS query lifecycle states (tags 0-4). @@ -62,7 +77,7 @@ public enum DnssecState: Int, CaseIterable, Sendable { case enabled = 1 /// DNSSEC key loaded. case keyLoaded = 2 - /// Response validated/signed. + /// Abstract ABI state; the current FFI cannot perform DNSSEC validation. case validated = 3 public init?(tag: UInt8) { self.init(rawValue: Int(tag)) } @@ -88,12 +103,13 @@ public enum DnssecAlgorithm: Int, CaseIterable, Sendable { // MARK: - Swift-idiomatic wrapper -/// Swift wrapper for the proven DNS server protocol FFI. +/// Swift wrapper for the bounded proven DNS message-builder FFI. /// /// Manages an opaque context slot in the Zig FFI pool. The context is /// automatically destroyed when this object is deallocated. /// -/// Lifecycle: Idle -> QueryReceived -> Lookup -> ResponseBuilding -> Sent. +/// Bounded message builder, not a general resolver. Only the exact 17-byte +/// standard root-question query subset is accepted; DNSSEC crypto fails closed. public final class ProvenDns: @unchecked Sendable { private let slot: Int32 @@ -134,11 +150,12 @@ public final class ProvenDns: @unchecked Sendable { /// The query class (ABI tag, 255 = unset). public var queryClass: UInt8 { dns_query_class(slot) } - /// Parse a DNS query from raw bytes. Transitions Idle -> QueryReceived. + /// Parse only the exact 17-byte standard root-question query subset. /// - /// - Parameter data: Raw DNS query bytes. - /// - Throws: ``ProvenError/invalidState`` if not in Idle state. + /// - Parameter data: Exactly 17 bytes for the standard root-question subset. + /// - Throws: ``ProvenError/invalidParameter`` for another length, or invalid state. public func parseQuery(_ data: Data) throws { + guard data.count == 17 else { throw ProvenError.invalidParameter } let result = data.withUnsafeBytes { buf -> UInt8 in let ptr = buf.baseAddress!.assumingMemoryBound(to: UInt8.self) return dns_parse_query(slot, ptr, UInt16(buf.count)) @@ -166,30 +183,30 @@ public final class ProvenDns: @unchecked Sendable { /// - rtype: Record type ABI tag. /// - rclass: Record class ABI tag. /// - ttl: Time-to-live in seconds. - /// - rdata: Record data bytes. - /// - Throws: ``ProvenError/invalidState`` if not in ResponseBuilding state. + /// - rdata: Record data bytes (at most 256 bytes). + /// - Throws: ``ProvenError/capacityExceeded`` for oversized data or invalid state. public func addAnswer(rtype: UInt8, rclass: UInt8, ttl: UInt32, rdata: Data) throws { - let result = rdata.withUnsafeBytes { buf -> UInt8 in - let ptr = buf.baseAddress!.assumingMemoryBound(to: UInt8.self) - return dns_add_answer(slot, rtype, rclass, ttl, ptr, UInt16(buf.count)) + guard rdata.count <= 256 else { throw ProvenError.capacityExceeded } + let result = withDnsRdataPointer(rdata) { ptr, len in + dns_add_answer(slot, rtype, rclass, ttl, ptr, len) } try ProvenError.checkStatus(result) } /// Add a resource record to the authority section. public func addAuthority(rtype: UInt8, rclass: UInt8, ttl: UInt32, rdata: Data) throws { - let result = rdata.withUnsafeBytes { buf -> UInt8 in - let ptr = buf.baseAddress!.assumingMemoryBound(to: UInt8.self) - return dns_add_authority(slot, rtype, rclass, ttl, ptr, UInt16(buf.count)) + guard rdata.count <= 256 else { throw ProvenError.capacityExceeded } + let result = withDnsRdataPointer(rdata) { ptr, len in + dns_add_authority(slot, rtype, rclass, ttl, ptr, len) } try ProvenError.checkStatus(result) } /// Add a resource record to the additional section. public func addAdditional(rtype: UInt8, rclass: UInt8, ttl: UInt32, rdata: Data) throws { - let result = rdata.withUnsafeBytes { buf -> UInt8 in - let ptr = buf.baseAddress!.assumingMemoryBound(to: UInt8.self) - return dns_add_additional(slot, rtype, rclass, ttl, ptr, UInt16(buf.count)) + guard rdata.count <= 256 else { throw ProvenError.capacityExceeded } + let result = withDnsRdataPointer(rdata) { ptr, len in + dns_add_additional(slot, rtype, rclass, ttl, ptr, len) } try ProvenError.checkStatus(result) } @@ -202,7 +219,7 @@ public final class ProvenDns: @unchecked Sendable { try ProvenError.checkStatus(dns_set_rcode(slot, rcodeTag)) } - /// Build the DNS response message. Transitions ResponseBuilding -> Sent. + /// Build a root-question response, capped at 512 bytes. /// /// - Returns: The serialised DNS response as `Data`. /// - Throws: ``ProvenError/invalidState`` if not in ResponseBuilding state. @@ -213,24 +230,24 @@ public final class ProvenDns: @unchecked Sendable { return Data(buf.prefix(Int(outLen))) } - /// Enable DNSSEC. Transitions Disabled -> Enabled. + /// Enable mode only; response construction then rejects because no signer exists. public func enableDnssec() throws { try ProvenError.checkStatus(dns_enable_dnssec(slot)) } - /// Load a DNSSEC signing key. Transitions Enabled -> KeyLoaded. + /// Always fails closed: the ABI accepts no private-key material. /// /// - Parameter algorithm: The DNSSEC algorithm to use. public func loadDnssecKey(algorithm: DnssecAlgorithm) throws { try ProvenError.checkStatus(dns_load_dnssec_key(slot, algorithm.tag)) } - /// Sign the response (DNSSEC). Transitions KeyLoaded -> Validated. + /// Always fails closed because no DNSSEC signing backend exists. public func signResponse() throws { try ProvenError.checkStatus(dns_sign_response(slot)) } - /// Check DNSSEC validation result. + /// Always returns false because no DNSSEC validator exists. public var isDnssecValid: Bool { dns_validate_dnssec(slot) == 0 } diff --git a/connectors/README.adoc b/connectors/README.adoc index c7d77ec3..7c111f63 100644 --- a/connectors/README.adoc +++ b/connectors/README.adoc @@ -1,137 +1,69 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -// -// connectors/README.adoc — Overview of the proven-servers connector interfaces. - -= Connector Interfaces += Connector Source Inventory :author: Jonathan D.A. Jewell :email: j.d.a.jewell@open.ac.uk -:revdate: 2026-03-01 - -== What Connectors Are - -Connectors define the **interface** between a formally verified server core -and external infrastructure. Each connector is a state machine: -connect, operate, disconnect — with every valid and invalid transition -proven at the type level. - -There are 6 connectors: - -[cols="1,2,1,1"] -|=== -| Connector | Purpose | States | Transitions - -| `proven-dbconn` -| Relational database connections (query, transaction, prepare) -| 5 -| 9 +:revdate: 2026-09-27 +:toc: -| `proven-authconn` -| Authentication lifecycle (MFA, tokens, lockout) -| 6 -| 11 +[IMPORTANT] +==== +This directory is a package inventory, not a set of verified connector +implementations. Names, model types, exported symbols, and historical design +notes do not establish that a connector builds, links, or interoperates with an +external service. +==== -| `proven-cacheconn` -| Cache connections (TTL, eviction, degradation) -| 4 -| 8 +== Current directories -| `proven-queueconn` -| Message queue pub/sub (subscribe, publish, ack/reject) -| 5 -| 11 +The current top-level connector directories are: -| `proven-resolverconn` -| DNS resolution (13 record types, DNSSEC, caching) -| 4 -| 9 +* `proven-cacheconn` +* `proven-dbconn` +* `proven-nesy-solver-api` +* `proven-queueconn` +* `proven-resolverconn` +* `proven-storageconn` -| `proven-storageconn` -| Object storage (upload, download, integrity checks) -| 5 -| 11 -|=== +There is no maintained `proven-authconn` directory. Connector contents and +language coverage vary; not every directory follows a common Idris2/Zig/C +layout. See each package manifest and README for its actual files and limits. -== Directory Structure +== Interface evidence -Each connector follows the same layout: +Some packages contain Idris2 model types or state-transition declarations, +Zig sources, hand-maintained/generated-looking C headers, and test targets. +Those are separate source artifacts. A successful model build would establish +only the checked Idris2 propositions; it would not prove that an independent +Zig implementation, C header, language binding, or external protocol conforms. -[source] ----- -connectors/proven-/ -├── src/ -│ ├── Conn.idr # Core types (existing skeleton) -│ ├── Conn/Types.idr # Sum types for the domain -│ ├── Conn/Main.idr # Entry point -│ ├── ConnABI.idr # Re-export module (ABI layer) -│ └── ConnABI/ -│ ├── Layout.idr # Tag encodings + roundtrip proofs -│ ├── Transitions.idr # GADT state machine + witnesses -│ └── Foreign.idr # Opaque handles + FFI contract -├── generated/abi/ -│ └── .h # C ABI header -└── ffi/zig/ - ├── build.zig # Zig build configuration - ├── src/.zig # Zig FFI implementation - └── test/_test.zig # Integration tests ----- - -== The ABI-FFI Pattern +No repository-wide header generator or ABI-conformance matrix currently +establishes correspondence across the connector packages. The +`proven-nesy-solver-api` package explicitly describes its FFI dispatch paths as +stubs and its HTTP/Fly.io work as experimental; deployment is disabled. Do not +interpret its endpoint or transport labels as implemented protocol support. -Each connector has three Idris2 ABI modules and a Zig FFI implementation. +== Package checks -=== Layer 1: Layout.idr (tag encodings) - -Encodes each sum type as `Bits8` tags. Provides an encoder, a decoder, -and a **roundtrip proof** ensuring they are inverse: - -[source,idris] ----- -storageOpToTag : StorageOp -> Bits8 -tagToStorageOp : Bits8 -> Maybe StorageOp -tagToStorageOpRoundtrip : (x : StorageOp) -> tagToStorageOp (storageOpToTag x) = Just x ----- - -=== Layer 2: Transitions.idr (state machine) - -A GADT encoding every legal state transition, **capability witnesses** -(predicates on which states allow certain operations), **impossibility -proofs** (eliminating bad states), and **decidability procedures** -(compile-time branching on capability). - -=== Layer 3: Foreign.idr (FFI contract) - -Opaque handle types via `[external]` and a documented function contract -listing every exported C-ABI function. - -=== Layer 4: Zig FFI (runtime enforcement) - -`enum(u8)` types matching the C header, handle structs tracking runtime -state, and exported `callconv(.c)` functions that enforce the state -machine by switching on the handle's current state. - -== Building +When toolchains are available, the root tasks discover current package +manifests and Zig build files: [source,bash] ---- -# Build any connector (shared + static libraries) -cd connectors/proven-dbconn/ffi/zig && zig build - -# Run tests -cd connectors/proven-authconn/ffi/zig && zig build test - -# Build all connectors -for conn in dbconn authconn cacheconn queueconn resolverconn storageconn; do - (cd connectors/proven-$conn/ffi/zig && zig build test) -done +just build-idris +just build-zig +just test-zig ---- -== Error Convention - -All connectors use tag `0` for "no error". Error variants start at tag `1`. -This matches the C convention where `0` indicates success. +These tasks require Idris2 and Zig; read their exit status and record exact +versions. This assessment workspace did not run the compiler-backed matrix. +For a focused change, inspect the package README and use its actual `.ipkg` or +`build.zig` command. No connector is declared production-ready here. -== See Also +== Related guidance -* link:../docs/design/DESIGN-2026-03-01-connector-abi-ffi.md[Design document] -* link:../ABI-FFI-README.md[ABI-FFI standard overview] +* `../ABI-FFI-README.adoc` — interface evidence boundaries. +* `../docs/design/DESIGN-2026-03-01-connector-abi-ffi.adoc` — dated design + note; inspect current source before relying on its assumptions. +* `../.machine_readable/BINDINGS.a2ml` — conservative binding inventory. +* `../READINESS.adoc` and `../PROOF-NEEDS.adoc` — current evidence and gaps. diff --git a/connectors/proven-nesy-solver-api/README.adoc b/connectors/proven-nesy-solver-api/README.adoc index baf69b8d..1c549228 100644 --- a/connectors/proven-nesy-solver-api/README.adoc +++ b/connectors/proven-nesy-solver-api/README.adoc @@ -1,105 +1,85 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -= proven-nesy-solver-api — Neurosymbolic Playground Backend += proven-nesy-solver-api — Experimental Connector :author: Jonathan D.A. Jewell :email: j.d.a.jewell@open.ac.uk -:revdate: 2026-04-05 +:revdate: 2026-09-27 :license: MPL-2.0 -== What This Is - -A type-safe backend interface for the nesy-solver.dev proof playground. - -Users submit proof obligations (SMT-LIB, Lean, Coq, Idris2, Agda); this -connector dispatches them to echidna, records the outcome in verisim-api's -`proof_attempts` table, and serves strategy recommendations back to the -frontend — all through a single request model exposed over 16 protocol -surfaces (REST, GraphQL, WebSocket, SSE, gRPC, JSON-RPC, MsgPack-RPC, -CBOR, Flatbuffers, Cap'n Proto, Bebop, tRPC, MQTT, AMQP, SOAP, VerisimDB). - -== Architecture +[IMPORTANT] +==== +This directory contains research/prototype source, not a verified proof service +or production-ready server. Compiler-backed builds and runtime/integration +checks have not been run in the current assessment. Directory contents and +source comments are not evidence that any endpoint, ABI, or external service +contract works. +==== + +== Scope + +The connector has an Idris2 package, a Zig C-ABI implementation, a hand-kept C +header, and a separate Zig HTTP-server prototype. Its source describes models +for prover kinds, input languages, obligation classes, outcomes, session state, +and protocol-surface labels. + +The `SurfaceKind` enum and the `/surfaces` response include sixteen names. That +is an inventory of labels, *not* sixteen implemented or interoperable protocol +transports. The HTTP prototype advertises REST-style routes in comments; their +build, behaviour, authentication, upstream integration, and failure handling +are not currently verified. + +The Idris2 `Layout.idr` source contains tag encoders/decoders and roundtrip +lemmas. These are source-level proof declarations only until Idris2 accepts the +package. They do not prove that the Zig implementation or hand-maintained C +header agrees with Idris2. No repository-wide ABI generation or conformance +check currently establishes that correspondence. + +The FFI Zig source explicitly describes dispatch and strategy functions as +stubs. The separate HTTP server source includes experimental integration code; +its presence does not establish that Echidna, VerisimDB/API, RGTV, or any other +upstream is reachable or follows the assumed contract. + +== Source inventory [source] ---- -src/ Idris2 ABI (formally proven) -├── NesySolverAPI.idr top-level re-export -├── NesySolverAPI/ -│ └── Types.idr ProverKind, InputLanguage, ObligationClass, -│ ProveOutcome, SessionState, SurfaceKind -├── NesySolverAPIABI.idr ABI re-export -├── NesySolverAPIABI/ -│ ├── Layout.idr Bits8 tag encodings + 6 roundtrip proofs -│ ├── Transitions.idr ValidTransition GADT + decidability -│ └── Foreign.idr opaque handles + FFI function contract -└── Main.idr demo executable - -generated/abi/ -└── nesy_solver_api.h C header (hand-maintained, must match - Layout.idr + nesy_solver_api.zig) - -ffi/zig/ Zig FFI implementation -├── build.zig zig build (shared + static libs) -├── src/nesy_solver_api.zig C-ABI functions + SHA-256 hasher -└── test/integration_test.zig cross-module integration tests - -zig/ Zig HTTP service + deployment -├── build.zig builds the nesy-server executable -├── src/main.zig REST + JSON proof-dispatch service -├── src/rgtv.zig RGTV grant-broker client -├── Containerfile podman/fly.io image -├── fly.toml fly.io deployment config -└── deploy-fly.sh deploy script +src/ Idris2 model and ABI declarations + NesySolverAPI/Types.idr data types and surface labels + NesySolverAPIABI/Layout.idr hand-written tag maps and roundtrip lemmas +generated/abi/ hand-maintained C header (not generated by a + checked repository tool) +ffi/zig/ C-ABI implementation and package tests +zig/ experimental HTTP server and grant-broker code ---- -== Type tag table (must stay synchronised) - -|=== -| Type | Tags | Source of truth - -| `ProverKind` | 0–8 | `src/NesySolverAPIABI/Layout.idr` -| `InputLanguage` | 0–4 | `src/NesySolverAPIABI/Layout.idr` -| `ObligationClass` | 0–10 | mirrors verisimdb `proof_attempts` Enum8 -| `ProveOutcome` | 0–3 | mirrors verisimdb `outcome` field -| `SessionState` | 0–3 | `src/NesySolverAPIABI/Transitions.idr` -| `SurfaceKind` | 0–15 | 16 zig unified-api-adapter protocol surfaces -|=== - -Any change to these tag values MUST update Layout.idr, nesy_solver_api.h, -and nesy_solver_api.zig in the same commit. - -== State machine (proof-dispatch session) +The README deliberately does not reproduce all source tag values: consumers +must inspect the current declarations, and cross-language correspondence must +be established by a reproducible generated-header/ABI test rather than by this +document. -[source] ----- -Idle --Submit--> Dispatching --Verdict--> Recording --RecordDone--> Idle - ^ | - | +-- DispatchFail --> FailedS --Reset--> Idle - | | - +--------------------Reset-------------------------------------+ ----- - -Every arrow corresponds to exactly one `ValidTransition` constructor. -The Idris2 type checker rejects any transition not listed. +== Available checks -== Build +Run from this directory when the toolchains are installed: [source,bash] ---- -# Idris2 types + demo executable idris2 --build proven-nesy-solver-api.ipkg -./build/exec/proven-nesy-solver-api - -# Zig FFI (produces libproven_nesy_solver_api.so + .a) -cd ffi/zig && zig build test --summary all +(cd ffi/zig && zig build test) +(cd zig && zig build test) ---- -== Phase E status +The current root task sweep and CI select only defined package targets; they do +not demonstrate that this service is deployable. No result is claimed here: +Idris2, Zig, and Just are unavailable in the current assessment workspace. + +== Deployment status + +Deployment is intentionally disabled. `zig/deploy-fly.sh` exits before invoking +Fly.io tools, `zig/fly.toml` contains no active app/build/service settings, and +the Zig `Containerfile` fails with an explicit message. This avoids accidental +cloud changes or publication of the currently mismatched, unverified image. -* *E2 (this session)*: Scaffold complete — Idris2 types + proofs + Zig FFI - skeleton + zig adapter. All dispatch functions are typed stubs. -* *E3 (next)*: Wire FFI to real echidna HTTP client (POST - `http://localhost:8090/api/verify`) and verisim-api persistence (POST - `http://localhost:8080/api/v1/proof_attempts`). Deploy adapter service - to fly.io. -* *E4*: V4 PROVEN/SANCTIFY certificate badges via - `GET /api/v1/proof_attempts/certificates`. +Do not enable deployment until the package builds, the HTTP server is tested +against contract fixtures, credential handling and authentication are reviewed, +the C ABI is checked against the Idris2 model and header, upstream integrations +are exercised, and a reproducible image/health-check pipeline is approved. diff --git a/connectors/proven-nesy-solver-api/generated/abi/nesy_solver_api.h b/connectors/proven-nesy-solver-api/generated/abi/nesy_solver_api.h index 445614f3..088b276f 100644 --- a/connectors/proven-nesy-solver-api/generated/abi/nesy_solver_api.h +++ b/connectors/proven-nesy-solver-api/generated/abi/nesy_solver_api.h @@ -4,21 +4,22 @@ * SPDX-License-Identifier: MPL-2.0 * Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) * - * proven-nesy-solver-api ABI -- C header mirroring Idris2 type definitions. - * DO NOT EDIT -- regenerate from src/NesySolverAPIABI/ if types change. + * proven-nesy-solver-api experimental C ABI header. * - * ABI Version: 0.1 + * This file is hand-maintained. No checked generator or repository-wide ABI + * conformance test currently establishes correspondence with the Idris2 and + * Zig declarations below. Update and verify all copies together when changing + * the interface; do not treat this header as generated evidence. * - * Tag values here MUST match src/NesySolverAPIABI/Layout.idr and - * ffi/zig/src/nesy_solver_api.zig exactly. + * ABI Version: 0.1 (declared) * - * Type tag consistency map: + * Type tag declarations: * ProverKind: tags 0-8 (9 provers) * InputLanguage: tags 0-4 (5 source languages) * ObligationClass: tags 0-10 (11 classes, mirrors verisimdb Enum8) * ProveOutcome: tags 0-3 (success/failure/timeout/unknown) * SessionState: tags 0-3 (Idle/Dispatching/Recording/FailedS) - * SurfaceKind: tags 0-15 (16 unified-api-adapter protocol surfaces) + * SurfaceKind: tags 0-15 (16 labels; transport implementations are not established) */ #ifndef PROVEN_NESY_SOLVER_API_H diff --git a/connectors/proven-nesy-solver-api/zig/Containerfile b/connectors/proven-nesy-solver-api/zig/Containerfile index fff23ec1..9dd47988 100644 --- a/connectors/proven-nesy-solver-api/zig/Containerfile +++ b/connectors/proven-nesy-solver-api/zig/Containerfile @@ -1,47 +1,13 @@ # SPDX-License-Identifier: MPL-2.0 -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# Copyright (c) Jonathan D.A. Jewell # -# Container for proven-nesy-solver-api Zig HTTP server. -# Replaces the legacy V-lang container at ../v/Containerfile. -# Deployed to fly.io; also used for local podman testing. -# -# Build: -# podman build -f Containerfile -t nesy-solver-api:0.1.0 . -# Run (local podman, against host echidna + verisim): -# podman run --rm -p 9000:9000 \ -# -e ECHIDNA_URL=http://host.containers.internal:8090 \ -# -e VERISIM_URL=http://host.containers.internal:8080 \ -# nesy-solver-api:0.1.0 - -# ─── Builder stage ───────────────────────────────────────────────────── -FROM docker.io/alpine:3.21 AS builder - -# Install Zig toolchain -ARG ZIG_VERSION=0.14.0 -RUN apk add --no-cache curl tar xz \ - && curl -sSL "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-x86_64-${ZIG_VERSION}.tar.xz" \ - | tar -xJ -C /opt \ - && ln -s "/opt/zig-linux-x86_64-${ZIG_VERSION}/zig" /usr/local/bin/zig +# DISABLED: this experimental server and its integration contracts have not +# been compiler-, runtime-, or deployment-verified. The legacy file used Zig +# 0.14.0 and expected a `nesy-server` artifact; build.zig now requires Zig +# 0.15.2+ and installs `nesy_server`. Do not build or publish an image from it. -WORKDIR /build -COPY build.zig ./ -COPY src/ ./src/ -RUN zig build -Doptimize=ReleaseFast - -# ─── Runtime stage ───────────────────────────────────────────────────── FROM docker.io/alpine:3.21 - -LABEL org.opencontainers.image.title="proven-nesy-solver-api" -LABEL org.opencontainers.image.description="Neurosymbolic proof playground backend (echidna + verisim-api bridge)" -LABEL org.opencontainers.image.licenses="MPL-2.0" -LABEL org.opencontainers.image.source="https://github.com/hyperpolymath/proven-servers" - -COPY --from=builder /build/zig-out/bin/nesy-server /usr/local/bin/nesy-server - -ENV NESY_PORT=9000 -ENV ECHIDNA_URL=http://localhost:8090 -ENV VERISIM_URL=http://localhost:8080 - -EXPOSE 9000 - -ENTRYPOINT ["/usr/local/bin/nesy-server"] +RUN printf '%s\n' \ + 'ERROR: proven-nesy-solver-api container packaging is not verified.' \ + 'Use the package-level tests; deployment remains disabled.' >&2; \ + exit 1 diff --git a/connectors/proven-nesy-solver-api/zig/deploy-fly.sh b/connectors/proven-nesy-solver-api/zig/deploy-fly.sh index 48b8f147..329ac280 100644 --- a/connectors/proven-nesy-solver-api/zig/deploy-fly.sh +++ b/connectors/proven-nesy-solver-api/zig/deploy-fly.sh @@ -1,301 +1,15 @@ #!/usr/bin/env bash # SPDX-License-Identifier: MPL-2.0 -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# Copyright (c) Jonathan D.A. Jewell # -# deploy-fly.sh: Deploy the nesy-solver playground stack to fly.io. -# -# Deploys in dependency order (all in the same fly.io org, reachable via -# .flycast 6PN DNS): -# 0. rgtv-nesy (rgtv-nesy) — credential broker, internal-only -# 1. ClickHouse (clickhouse-nesy) — volume + schema migration -# 2. verisim-api (verisim-api) — HTTP API, depends on ClickHouse -# 3. echidna (echidna-nesy) — prover dispatcher, standalone -# 4. nesy-solver-api — public edge, depends on (0)+(2)+(3) -# -# Prereqs: -# - flyctl auth login (interactive browser OAuth; set FLY_API_TOKEN instead -# for non-interactive runs) -# - billing card attached to the fly.io org (trial no longer applies) -# -# Run: -# ./deploy-fly.sh [phase] -# phase = all (default) -# | rgtv (deploy RGTV broker and set its secrets) -# | clickhouse | schema | verisim | echidna | nesy-api -# | secrets (re-set VERISIM_CLICKHOUSE_URL from saved password) -# -# Idempotent: app-create, volume-create, and schema steps all skip when -# already present. Re-running `all` is safe. -# -# State files (gitignored): -# ~/.config/nesy-solver/fly-ch-password — random password picked for -# ClickHouse. Save this; if lost -# you must redeploy with a new one. +# Disabled: the experimental Fly.io deployment script is not a validated or +# authorized production release path. It previously orchestrated multiple +# external apps/services and could incur charges or modify live resources. +# This guard intentionally runs before checking for or invoking flyctl. set -euo pipefail - -REGION="${FLY_REGION:-lhr}" -ORG="${FLY_ORG:-personal}" -RGTV_APP="rgtv-nesy" -CH_APP="clickhouse-nesy" -VERISIM_APP="verisim-api" -ECHIDNA_APP="echidna-nesy" -NESY_APP="nesy-solver-api" - -# Repo paths (absolute, so the script works from any cwd). -REPOS_ROOT="$(cd "$(dirname "$0")/../../../.." && pwd)" -RGTV="$REPOS_ROOT/reasonably-good-token-vault/vault-broker" -VERISIMDB="$REPOS_ROOT/verisimdb" -ECHIDNA="$REPOS_ROOT/echidna" -NESY_API="$REPOS_ROOT/proven-servers/connectors/proven-nesy-solver-api/v" - -# State directory for locally-generated secrets we need to reuse. -STATE_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/nesy-solver" -RGTV_AGENT_TOKEN_FILE="$STATE_DIR/rgtv-agent-token" - -CH_PW_FILE="$STATE_DIR/fly-ch-password" - -PHASE="${1:-all}" - -say() { printf "\n\033[1;34m▶ %s\033[0m\n" "$*"; } -ok() { printf " \033[1;32m✓\033[0m %s\n" "$*"; } -warn() { printf " \033[1;33m!\033[0m %s\n" "$*"; } -die() { printf " \033[1;31m✗\033[0m %s\n" "$*" >&2; exit 1; } - -ensure_app() { - local app="$1" - if flyctl apps list -j 2>/dev/null | grep -q "\"Name\": *\"$app\""; then - ok "app $app exists" - else - say "creating app $app" - flyctl apps create "$app" --org "$ORG" - fi -} - -ensure_volume() { - local app="$1" name="$2" size="$3" - if flyctl volumes list -a "$app" -j 2>/dev/null | grep -q "\"name\": *\"$name\""; then - ok "volume $name exists on $app" - else - say "creating volume $name (${size}GB) on $app" - flyctl volumes create "$name" --size "$size" --region "$REGION" -a "$app" --yes - fi -} - -# Start flyctl proxy and wait until the port is actually accepting connections -# (default 20s budget). Echoes the proxy PID on stdout on success. -wait_for_proxy() { - local app="$1" local_port="$2" remote_port="$3" - flyctl proxy "${local_port}:${remote_port}" -a "$app" >/tmp/fly-proxy-${app}.log 2>&1 & - local pid=$! - local tries=0 - while [ $tries -lt 20 ]; do - if curl -sS --max-time 1 "http://localhost:${local_port}/ping" >/dev/null 2>&1 \ - || curl -sS --max-time 1 "http://localhost:${local_port}/" >/dev/null 2>&1; then - echo "$pid" - return 0 - fi - sleep 1 - tries=$((tries + 1)) - done - kill "$pid" 2>/dev/null || true - die "flyctl proxy to $app:$remote_port did not come up in 20s (see /tmp/fly-proxy-${app}.log)" -} - -load_or_pick_ch_password() { - mkdir -p "$STATE_DIR" - chmod 700 "$STATE_DIR" - if [ -f "$CH_PW_FILE" ]; then - cat "$CH_PW_FILE" - else - local pw - pw="$(openssl rand -hex 24)" - echo "$pw" > "$CH_PW_FILE" - chmod 600 "$CH_PW_FILE" - echo "$pw" - fi -} - -load_or_pick_rgtv_token() { - mkdir -p "$STATE_DIR" - chmod 700 "$STATE_DIR" - if [ -f "$RGTV_AGENT_TOKEN_FILE" ]; then - cat "$RGTV_AGENT_TOKEN_FILE" - else - local tok - tok="$(openssl rand -hex 32)" - echo "$tok" > "$RGTV_AGENT_TOKEN_FILE" - chmod 600 "$RGTV_AGENT_TOKEN_FILE" - echo "$tok" - fi -} - -deploy_rgtv() { - say "=== Phase 0: RGTV credential broker ===" - ensure_app "$RGTV_APP" - - local agent_tok - agent_tok="$(load_or_pick_rgtv_token)" - ok "agent token saved to $RGTV_AGENT_TOKEN_FILE" - - # NESY_INGEST_TOKEN is pulled from the caller's env or the saved state file. - local ingest_tok="${NESY_INGEST_TOKEN:-}" - if [ -z "$ingest_tok" ]; then - die "NESY_INGEST_TOKEN must be set in env to register with RGTV" - fi - - flyctl secrets set \ - RGTV_AGENT_TOKEN="$agent_tok" \ - RGTV_CRED_NESY_INGEST_TOKEN="$ingest_tok" \ - -a "$RGTV_APP" --stage >/dev/null - ok "RGTV secrets staged" - - cd "$RGTV" - flyctl deploy -a "$RGTV_APP" -c fly.toml --dockerfile Containerfile --remote-only - ok "rgtv-nesy deployed" - - # Wire the agent token into nesy-solver-api so it can authenticate to RGTV. - flyctl secrets set \ - RGTV_AGENT_TOKEN="$agent_tok" \ - -a "$NESY_APP" --stage >/dev/null 2>&1 || true - ok "RGTV_AGENT_TOKEN staged on $NESY_APP (will apply on next deploy)" -} - -deploy_clickhouse() { - say "=== Phase 1: ClickHouse ===" - ensure_app "$CH_APP" - ensure_volume "$CH_APP" clickhouse_data 1 - - local pw - pw="$(load_or_pick_ch_password)" - flyctl secrets set CLICKHOUSE_PASSWORD="$pw" -a "$CH_APP" --stage >/dev/null 2>&1 \ - || warn "CLICKHOUSE_PASSWORD already set or stage failed" - ok "password saved to $CH_PW_FILE" - - cd "$VERISIMDB" - flyctl deploy -a "$CH_APP" -c clickhouse.fly.toml --remote-only - ok "clickhouse deployed" -} - -apply_schema() { - say "=== Phase 1b: Apply ClickHouse schema ===" - cd "$VERISIMDB" - local pw - pw="$(load_or_pick_ch_password)" - - local proxy_pid - proxy_pid=$(wait_for_proxy "$CH_APP" 18123 8123) - ok "proxy up on localhost:18123" - # shellcheck disable=SC2064 - trap "kill $proxy_pid 2>/dev/null || true" EXIT - - # ClickHouse HTTP rejects multi-statement bodies by default. Split the - # schema file on semicolons and POST each statement separately. - local tmpdir - tmpdir=$(mktemp -d) - csplit -z -s -f "$tmpdir/stmt-" -b '%02d.sql' \ - deploy/nesy-playground-schema.sql '/;$/+1' '{*}' || true - local applied=0 - for f in "$tmpdir"/stmt-*.sql; do - local stmt - stmt=$(sed 's/^--.*$//' "$f" | tr '\n' ' ' | sed -E 's/;\s*$//' | sed -E 's/^\s+//;s/\s+$//') - if [ -n "$stmt" ]; then - curl -sSf -u "verisim:$pw" 'http://localhost:18123/' --data-binary "$stmt" >/dev/null - applied=$((applied + 1)) - fi - done - rm -rf "$tmpdir" - - kill "$proxy_pid" 2>/dev/null || true - trap - EXIT - ok "schema applied ($applied statements)" -} - -deploy_verisim() { - say "=== Phase 2: verisim-api ===" - ensure_app "$VERISIM_APP" - # Now that the app exists, wire the ClickHouse connection URL. - local pw - pw="$(load_or_pick_ch_password)" - # NB: URL has NO path suffix — verisim-api issues queries against fully- - # qualified table names (`INSERT INTO verisimdb.proof_attempts ...`), so - # the base URL must not include `/verisimdb`. - flyctl secrets set \ - VERISIM_CLICKHOUSE_URL="http://verisim:${pw}@${CH_APP}.internal:8123" \ - VERISIM_GRPC_PORT="0" \ - -a "$VERISIM_APP" --stage >/dev/null - ok "verisim-api secrets staged" - - cd "$VERISIMDB" - flyctl deploy -a "$VERISIM_APP" -c fly.toml --dockerfile Containerfile.api --remote-only - ok "verisim-api deployed" -} - -deploy_echidna() { - say "=== Phase 3: echidna ===" - ensure_app "$ECHIDNA_APP" - cd "$ECHIDNA" - flyctl deploy -a "$ECHIDNA_APP" -c fly.toml \ - --dockerfile .containerization/Containerfile.full --remote-only - ok "echidna deployed" -} - -deploy_nesy_api() { - say "=== Phase 4: nesy-solver-api ===" - ensure_app "$NESY_APP" - cd "$NESY_API" - flyctl deploy -a "$NESY_APP" -c fly.toml --dockerfile Containerfile --remote-only - ok "nesy-solver-api deployed" - local hostname="https://${NESY_APP}.fly.dev" - ok "public URL: $hostname" - # Warm the machine so the health probe succeeds. - curl -sS --max-time 10 "$hostname/health" >/dev/null || true -} - -set_secrets_only() { - say "=== Re-setting secrets from saved state ===" - local pw - pw="$(load_or_pick_ch_password)" - flyctl secrets set \ - VERISIM_CLICKHOUSE_URL="http://verisim:${pw}@${CH_APP}.internal:8123" \ - VERISIM_GRPC_PORT="0" \ - -a "$VERISIM_APP" >/dev/null - ok "verisim-api secrets updated (machine will restart)" -} - -main() { - if ! command -v flyctl >/dev/null 2>&1; then - die "flyctl not in PATH — add: export PATH=\"\$HOME/.fly/bin:\$PATH\"" - fi - if ! flyctl auth whoami >/dev/null 2>&1; then - die "not authenticated — run: flyctl auth login" - fi - - case "$PHASE" in - rgtv) deploy_rgtv ;; - clickhouse) deploy_clickhouse ;; - schema) apply_schema ;; - verisim) deploy_verisim ;; - echidna) deploy_echidna ;; - nesy-api) deploy_nesy_api ;; - secrets) set_secrets_only ;; - all) - deploy_rgtv - deploy_clickhouse - apply_schema - deploy_verisim - deploy_echidna - deploy_nesy_api - say "=== Deploy complete ===" - ok "public URL: https://${NESY_APP}.fly.dev" - ok "ClickHouse password: $CH_PW_FILE" - ok "RGTV agent token: $RGTV_AGENT_TOKEN_FILE" - ;; - *) - echo "usage: $0 [all|clickhouse|schema|verisim|echidna|nesy-api|secrets]" - exit 1 - ;; - esac -} - -main "$@" +printf '%s\n' \ + 'ERROR: Fly.io deployment is disabled for proven-nesy-solver-api.' \ + 'The Zig image and upstream integrations are not verified or release-ready.' \ + 'No cloud commands were run. See this connector README and root READINESS.adoc.' >&2 +exit 2 diff --git a/connectors/proven-nesy-solver-api/zig/fly.toml b/connectors/proven-nesy-solver-api/zig/fly.toml index 848b7c2b..709e60b5 100644 --- a/connectors/proven-nesy-solver-api/zig/fly.toml +++ b/connectors/proven-nesy-solver-api/zig/fly.toml @@ -1,54 +1,6 @@ # SPDX-License-Identifier: MPL-2.0 -# fly.io deployment configuration for proven-nesy-solver-api (Zig build). # -# Replaces the legacy V-lang deployment at ../v/fly.toml. -# -# Deploy: -# cd proven-servers/connectors/proven-nesy-solver-api/zig -# flyctl launch --no-deploy --copy-config # first time only -# flyctl deploy -# -# Private networking: echidna-nesy and verisim-api are reached via Fly's -# private 6PN network. Use .flycast (NOT .internal) so that auto-stopped -# machines are woken on first request. .internal does NOT trigger auto-wake. - -app = "nesy-solver-api" -primary_region = "lhr" # London — nearest to the .dev registrar region -kill_signal = "SIGTERM" -kill_timeout = "10s" - -[build] - dockerfile = "Containerfile" - -[env] - NESY_PORT = "9000" - NESY_REPO_TAG = "hyperpolymath/nesy-solver" - NESY_FILE_TAG = "playground/submission.txt" - # .flycast (not .internal) — required for auto-wake of stopped machines. - ECHIDNA_URL = "http://echidna-nesy.flycast:8090" - VERISIM_URL = "http://verisim-api.flycast:8080" - # RGTV grant broker — set RGTV_URL + RGTV_AGENT_TOKEN as fly secrets to - # enable credential brokering. If unset, falls back to NESY_INGEST_TOKEN env. - RGTV_URL = "http://rgtv-nesy.flycast:9100" - # RGTV_AGENT_TOKEN set via: flyctl secrets set RGTV_AGENT_TOKEN=... -a nesy-solver-api - -[http_service] - internal_port = 9000 - force_https = true - auto_stop_machines = "stop" - auto_start_machines = true - min_machines_running = 0 - processes = ["app"] - - [[http_service.checks]] - interval = "30s" - timeout = "5s" - grace_period = "10s" - method = "GET" - path = "/health" - -[[vm]] - size = "shared-cpu-1x" - memory = "256mb" - cpu_kind = "shared" - cpus = 1 +# DISABLED: no Fly.io app, image, health checks, secrets, or upstream services +# have been validated for this experimental connector. There is deliberately no +# `app`, `[build]`, or `[http_service]` configuration here. Do not run flyctl +# from this directory; deploy-fly.sh refuses to execute. diff --git a/container/.gatekeeper.yaml b/container/.gatekeeper.yaml deleted file mode 100644 index 7ed2d240..00000000 --- a/container/.gatekeeper.yaml +++ /dev/null @@ -1,122 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# -# Svalinn gatekeeper policy for proven-servers -# -# Controls which operations are permitted through the edge gateway. -# This template provides moderate security defaults — not wide-open test -# mode, but not production-hardened either. Tighten the values below -# before deploying to production. -# -# See: stapeln/container-stack/svalinn/ - -version: "1.0" - -# ============================================================================ -# Authentication -# ============================================================================ -# -# Define which endpoints require authentication and at what level. - -auth: - # Public endpoints — no authentication required. - # Health and readiness probes must always be public so that - # orchestrators (selur, Podman, k8s) can check service status. - public: - - path: "/health" - methods: ["GET"] - - path: "/ready" - methods: ["GET"] - - path: "/metrics" - methods: ["GET"] - - # Endpoints requiring JWT or OAuth2 authentication. - # Svalinn validates the token before forwarding the request. - authenticated: - - path: "/api/v1/*" - methods: ["GET", "POST", "PUT", "DELETE"] - -# ============================================================================ -# Rate Limiting -# ============================================================================ -# -# Protects backend services from overload. Values here are moderate -# defaults — adjust based on your service capacity. - -rate_limits: - # Global limit: applied to all authenticated clients. - global: - requests_per_second: 500 - burst: 1000 - - # Write operations: stricter limit to protect data stores. - writes: - paths: ["/api/v1/*"] - methods: ["POST", "PUT", "DELETE"] - requests_per_second: 100 - burst: 200 - -# ============================================================================ -# Container Trust -# ============================================================================ -# -# Svalinn verifies that all .ctp bundles in the stack are signed by -# trusted keys and carry the required attestations. - -trust: - # Only accept .ctp bundles signed by these keys. - trusted_signers: - - key_id: "proven-servers-release" - algorithm: "Ed25519" - public_key_file: "/etc/svalinn/keys/proven-servers-release.pub" - - # Require these attestations on all .ctp bundles. - required_attestations: - - "source-signature" - - "sbom-complete" - - # Reject unsigned or untrusted images. - reject_unsigned: true - -# ============================================================================ -# Request Validation -# ============================================================================ -# -# Input validation at the gateway layer — catches malformed requests -# before they reach the application. - -validation: - # Maximum request body size. - max_body_size: "8MB" - - # Reject requests with NaN or Infinity in numeric fields. - reject_nan_inf: true - - # Maximum result limit per list/search query. - max_result_limit: 500 - -# ============================================================================ -# CORS -# ============================================================================ -# -# Cross-Origin Resource Sharing policy. The defaults below allow all -# origins — restrict to your frontend domain(s) in production. - -cors: - allow_origins: ["*"] - allow_methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"] - allow_headers: ["Content-Type", "Authorization"] - max_age: 3600 - -# ============================================================================ -# Logging -# ============================================================================ -# -# Structured logging for svalinn itself. Audit paths log all requests -# (including body hashes) for post-incident investigation. - -logging: - format: "json" - level: "info" - # Log all write operations for audit trail. - audit_paths: - - "/api/v1/*" diff --git a/container/0-AI-MANIFEST.a2ml b/container/0-AI-MANIFEST.a2ml deleted file mode 100644 index ccb5bc51..00000000 --- a/container/0-AI-MANIFEST.a2ml +++ /dev/null @@ -1,143 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 ---- -### [META] -id: "container-templates" -version: "1.0.0" -context: - - "https://a2ml.org/ns/v2" - - "https://stapeln.dev/ns/v1" - ---- -### [AI_MANIFEST] -description: | - Container templates for the stapeln container ecosystem. This directory - provides Podman-Chainguard-stapeln templates that are customised via - `just container-init` or `just init` during project bootstrap. - - All files use {{PLACEHOLDER}} tokens that are substituted with project- - specific values during initialisation. - -purpose: | - Provide a complete, security-first container deployment story for any - RSR-compliant repository. The templates cover the full lifecycle: - build, sign, verify, deploy, monitor, and govern. - -canonical_locations: - compose: "container/compose.toml" - containerfile: "container/Containerfile" - manifest: "container/manifest.toml" - gatekeeper: "container/.gatekeeper.yaml" - build_pipeline: "container/ct-build.sh" - entrypoint: "container/entrypoint.sh" - monitoring: "container/vordr.toml" - deployment: "container/deploy.k9.ncl" - example: "container/compose.example.toml" - ---- -### [FILE_RELATIONSHIPS] -files: - - name: "compose.toml" - role: "Orchestration" - description: | - selur-compose stack definition. Declares services, volumes, networks, - and health checks. References the Containerfile for image builds and - .gatekeeper.yaml for svalinn policy. - depends_on: ["Containerfile", ".gatekeeper.yaml"] - - - name: "Containerfile" - role: "Image Build" - description: | - Multi-stage OCI container build. Stage 1 compiles the application on - wolfi-base; Stage 2 copies the binary into a minimal runtime image. - Copies entrypoint.sh, .gatekeeper.yaml, and manifest.toml into the - final image. - depends_on: ["entrypoint.sh", ".gatekeeper.yaml", "manifest.toml"] - - - name: "manifest.toml" - role: "Bundle Metadata" - description: | - Cerro-torre .ctp bundle manifest. Describes provenance, dependencies, - attestations, and runtime security profile. Used by `ct pack` and - `ct verify`. - depends_on: [] - - - name: ".gatekeeper.yaml" - role: "Gateway Policy" - description: | - Svalinn edge gateway policy. Controls authentication, rate limiting, - container trust, request validation, CORS, and audit logging. - depends_on: [] - - - name: "ct-build.sh" - role: "Build Pipeline" - description: | - Shell script implementing the 5-stage pipeline: build (Podman), - pack (cerro-torre .ctp), sign (Ed25519), verify, push (optional). - Degrades gracefully when cerro-torre tools are not installed. - depends_on: ["Containerfile", "manifest.toml"] - - - name: "entrypoint.sh" - role: "Container Entrypoint" - description: | - Startup script with signal handling (SIGTERM, SIGINT), logging, and - exec into the main application process. - depends_on: [] - - - name: "vordr.toml" - role: "Runtime Monitoring" - description: | - Vordr monitoring configuration. Health endpoint probing, crash - detection, resource thresholds, and structured log output. - depends_on: [] - - - name: "deploy.k9.ncl" - role: "Deployment Component" - description: | - k9-svc deployment specification at Hunt trust level. Full pedigree - (L1-L5), environment configs, container config, and rolling - deployment strategy. - depends_on: ["compose.toml", "ct-build.sh"] - - - name: "compose.example.toml" - role: "Example" - description: | - Fully-commented multi-service example (Rust API + Elixir worker + - svalinn gateway). Copy to compose.toml and customise. - depends_on: [] - ---- -### [STAPELN_ECOSYSTEM] -overview: | - The stapeln container ecosystem comprises six tools: - - selur — Container orchestration with zero-copy IPC. Reads compose.toml. - cerro-torre — Verified container packaging (.ctp bundles), Ed25519 signing. - svalinn — Policy-driven edge gateway (auth, rate limits, CORS, trust). - vordr — Runtime monitoring (health, crashes, resources, logs). - rokur — Secrets management (runtime injection, no baked secrets). - k9-svc — Nickel deployment components (Kennel/Yard/Hunt trust levels). - -invariants: - - "Base images MUST be cgr.dev/chainguard/wolfi-base or cgr.dev/chainguard/static" - - "Container runtime is Podman — never Docker" - - "Containerfile — never Dockerfile" - - "All images run as non-root (appuser or project-specific user)" - - ".ctp bundles are signed with Ed25519 via cerro-torre" - - "Health endpoints (/health, /ready) must always be public (no auth)" - ---- -### [USAGE] -initialisation: | - Run `just container-init` to substitute all {{PLACEHOLDER}} tokens with - project-specific values. This is also run as part of `just init`. - -development: | - 1. `just container-build` — Build the container image - 2. `just container-verify` — Verify compose configuration - 3. `just container-up` — Start the stack locally - 4. `just container-down` — Stop the stack - -production: | - 1. `just container-sign` — Build, sign, verify .ctp bundle - 2. `just container-push` — Push signed bundle to registry - 3. `selur-compose up` — Deploy on target host diff --git a/container/Containerfile b/container/Containerfile deleted file mode 100644 index 2e2fc8f2..00000000 --- a/container/Containerfile +++ /dev/null @@ -1,136 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# proven-servers Container Image -# -# Multi-stage build template for Chainguard Wolfi base images. -# Customise the builder stage for your language and copy the -# resulting binary/release into the minimal runtime stage. -# -# Build with Podman: -# podman build -t proven-servers:latest -f container/Containerfile . -# -# Run: -# podman run -p 8080:8080 proven-servers:latest -# -# Run with persistent volume: -# podman run -p 8080:8080 -v proven-servers-data:/data proven-servers:latest - -# ============================================================================ -# Stage 1: Builder -# ============================================================================ -# -# Install build tools and compile the application. -# This stage is discarded after the build — only the compiled output -# is copied into the runtime stage. -# -# Language-specific examples (uncomment the one you need): -# -# --- Rust --- -# RUN apk add --no-cache rust pkgconf build-base -# COPY Cargo.toml Cargo.lock ./ -# COPY src/ ./src/ -# RUN cargo build --release -# # Output: /build/target/release/proven-servers -# -# --- Elixir --- -# RUN apk add --no-cache erl27-elixir-1.18 erlang-27 erlang-27-dev git build-base -# COPY mix.exs mix.lock ./ -# COPY lib/ ./lib/ -# COPY config/ ./config/ -# ENV MIX_ENV=prod -# RUN mix local.hex --force && mix local.rebar --force && \ -# mix deps.get --only prod && mix compile && mix release -# # Output: /build/_build/prod/rel/proven-servers/ -# -# --- Zig --- -# RUN apk add --no-cache zig build-base -# COPY build.zig build.zig.zon ./ -# COPY src/ ./src/ -# RUN zig build -Doptimize=ReleaseFast -# # Output: /build/zig-out/bin/proven-servers -# -FROM cgr.dev/chainguard/wolfi-base:latest AS builder - -# TODO: Install your language toolchain -RUN apk add --no-cache build-base - -WORKDIR /build - -# TODO: Copy source files and build -COPY . . -# RUN - -# ============================================================================ -# Stage 2: Runtime -# ============================================================================ -# -# Minimal production image. Only the compiled binary/release and runtime -# dependencies are included. No compilers, no source code, no build tools. -# -FROM cgr.dev/chainguard/wolfi-base:latest - -# OCI image labels (compatible with cerro-torre .ctp bundle metadata) -LABEL org.opencontainers.image.title="proven-servers" \ - org.opencontainers.image.description="Formally verified server components in Idris 2 with Zig FFI" \ - org.opencontainers.image.url="https://github.com/hyperpolymath/proven-servers" \ - org.opencontainers.image.source="https://github.com/hyperpolymath/proven-servers" \ - org.opencontainers.image.vendor="hyperpolymath" \ - org.opencontainers.image.licenses="MPL-2.0" \ - org.opencontainers.image.authors="Jonathan D.A. Jewell " \ - dev.cerrotorre.manifest="container/manifest.toml" \ - dev.cerrotorre.gatekeeper="container/.gatekeeper.yaml" \ - dev.stapeln.compose="container/compose.toml" - -# Install minimal runtime dependencies. -# Adjust this list for your application: -# - ca-certificates: TLS root certificates -# - curl: health check probe -# - libstdc++: C++ standard library (if needed by native deps) -# - ncurses: terminal UI (if needed, e.g. Elixir IEx) -RUN apk add --no-cache ca-certificates curl - -# Create non-root user for the application. -# Running as root inside containers is a security anti-pattern. -RUN addgroup -S appuser && adduser -S appuser -G appuser - -WORKDIR /app - -# TODO: Copy compiled binary/release from builder stage. -# Examples: -# COPY --from=builder /build/target/release/proven-servers /app/proven-servers -# COPY --from=builder /build/_build/prod/rel/proven-servers /app/release/ -# COPY --from=builder /build/zig-out/bin/proven-servers /app/proven-servers - -# Copy entrypoint script -COPY container/entrypoint.sh /app/entrypoint.sh -RUN chmod +x /app/entrypoint.sh - -# Copy stapeln integration files (svalinn gatekeeper policy, cerro-torre manifest) -COPY container/.gatekeeper.yaml /etc/svalinn/gatekeeper.yaml -COPY container/manifest.toml /app/manifest.toml - -# Create data directory for persistent storage (mountable volume) -RUN mkdir -p /data && chown appuser:appuser /data - -# Set ownership of the application directory -RUN chown -R appuser:appuser /app - -# Environment variables — customise for your application -ENV APP_HOST=[::] -ENV APP_PORT=8080 -ENV APP_LOG_FORMAT=json -ENV APP_DATA_DIR=/data - -# Declare /data as a volume for persistent storage -VOLUME ["/data"] - -# Run as non-root -USER appuser - -# Expose the application port -EXPOSE 8080 - -# Health check — the application must respond 2xx at /health -HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ - CMD curl -sf http://localhost:${APP_PORT}/health || exit 1 - -ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/container/README.adoc b/container/README.adoc deleted file mode 100644 index 3e7e148f..00000000 --- a/container/README.adoc +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= proven-servers Container Templates -:toc: left -:toclevels: 3 -:sectnums: - -== Overview - -This directory contains container templates for the -https://github.com/hyperpolymath/stapeln[stapeln] container ecosystem. -The stapeln stack provides verified container packaging, edge gateway -policies, runtime monitoring, and supply-chain signing for Podman-based -deployments using https://www.chainguard.dev/[Chainguard] Wolfi base images. - -All files use `{{PLACEHOLDER}}` tokens that are replaced by `just container-init` -(or by the top-level `just init` during project bootstrap). - -== File Reference - -[cols="1,3"] -|=== -| File | Purpose - -| `compose.toml` -| **selur-compose** stack definition. Declares services, volumes, networks, - and health checks. The primary orchestration file for local and production - deployment. Use `selur-compose up` or fall back to `podman compose`. - -| `compose.example.toml` -| Concrete multi-service example with detailed comments. Copy and customise - for your own stack. Not used directly by any tooling. - -| `Containerfile` -| Multi-stage OCI container build specification. Stage 1 builds the - application; Stage 2 produces a minimal runtime image on - `cgr.dev/chainguard/wolfi-base`. Uses Podman (never Docker). - -| `manifest.toml` -| **cerro-torre** bundle metadata. Describes the `.ctp` verified container - package: provenance, dependencies, attestations, and runtime security - profile. Used by `ct pack` and `ct verify`. - -| `.gatekeeper.yaml` -| **svalinn** edge gateway policy. Controls authentication, rate limiting, - container trust, request validation, CORS, and audit logging at the - network boundary. - -| `ct-build.sh` -| Build, sign, and verify pipeline script. Five stages: build (Podman), - pack (cerro-torre `.ctp`), sign (Ed25519), verify, and push (optional). - Gracefully degrades when cerro-torre tools are not installed. - -| `entrypoint.sh` -| Container entrypoint with signal handling (SIGTERM, SIGINT), startup - logging, and `exec` into the main application process. - -| `vordr.toml` -| **vordr** runtime monitoring configuration. Defines health endpoints, - crash detection, resource thresholds, and log output. - -| `deploy.k9.ncl` -| **k9-svc** deployment component at Hunt trust level. Full pedigree - (L1--L5), environment configs (dev/staging/prod), container - configuration, and rolling deployment strategy. - -| `0-AI-MANIFEST.a2ml` -| AI-readable manifest describing the container directory, file - interconnections, and the stapeln ecosystem. -|=== - -== The stapeln Ecosystem - -The stapeln container ecosystem comprises six interconnected tools: - -**selur** (compose):: - Container orchestration with zero-copy IPC for co-located services. - Reads `compose.toml` files. Falls back to standard Podman Compose - when the selur driver is unavailable. - -**cerro-torre** (bundles and signing):: - Verified container packaging. Produces `.ctp` bundles from OCI images, - signs them with Ed25519, and verifies the full chain. Tools: `ct pack`, - `ct sign`, `ct verify`, `ct push`, `ct explain`. - -**svalinn** (edge gateway):: - Policy-driven reverse proxy. Enforces authentication, rate limiting, - CORS, and container trust policies defined in `.gatekeeper.yaml`. - -**vordr** (monitoring):: - Runtime container monitoring. Watches health endpoints, detects crashes, - tracks resource usage, and emits structured logs. - -**rokur** (secrets):: - Secrets management for container deployments. Injects secrets at runtime - without baking them into images. Currently a stub/placeholder. - -**k9-svc** (deployment components):: - Nickel-based deployment specification. Components declare their pedigree - (identity, target, security, validation, recipes) and execute at one of - three trust levels: Kennel (data only), Yard (evaluation), Hunt (full - execution with cryptographic handshake). - -== How to Initialise - -[source,bash] ----- -# Option 1: During project bootstrap (includes all placeholders) -just init - -# Option 2: Container-specific initialisation -just container-init ----- - -The `container-init` recipe prompts for container-specific values -(service name, port, registry) and substitutes all `{{PLACEHOLDER}}` -tokens in the `container/` directory. - -== Development Workflow - -[source,bash] ----- -# 1. Build the container image -just container-build - -# 2. Verify the compose configuration -just container-verify - -# 3. Start the stack locally -just container-up --detach - -# 4. Check logs -podman compose --file container/compose.toml logs -f - -# 5. Stop the stack -just container-down ----- - -== Production Deployment - -[source,bash] ----- -# 1. Build, sign, and verify the .ctp bundle -just container-sign - -# 2. Push the signed bundle to the registry -just container-push - -# 3. Deploy on the target host -selur-compose up --detach ----- - -For k9-svc managed deployments: - -[source,bash] ----- -# Validate the deployment component -nickel typecheck container/deploy.k9.ncl - -# Deploy (requires Hunt-level authorisation) -k9-svc deploy container/deploy.k9.ncl --env production ----- - -== Base Images - -All Containerfiles use Chainguard Wolfi base images: - -* **Builder stage:** `cgr.dev/chainguard/wolfi-base:latest` -* **Runtime stage:** `cgr.dev/chainguard/wolfi-base:latest` (or - `cgr.dev/chainguard/static:latest` for statically-linked binaries) - -Chainguard images are minimal, CVE-free, and rebuilt daily. They use the -`apk` package manager (Alpine-compatible). - -== Container Runtime - -This project uses **Podman** (never Docker). All scripts, compose files, -and documentation reference Podman commands. The OCI Containerfile format -is compatible with Podman, Docker, and nerdctl. diff --git a/container/compose.example.toml b/container/compose.example.toml deleted file mode 100644 index d8d717c4..00000000 --- a/container/compose.example.toml +++ /dev/null @@ -1,135 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# -# Example selur-compose configuration — multi-service stack -# -# This is a concrete, fully-commented example showing a Rust API + Elixir -# worker + svalinn gateway deployment. Copy this file to compose.toml and -# customise for your project. -# -# Usage: -# cp compose.example.toml compose.toml -# # Edit service names, ports, images -# selur-compose up --detach - -version = "1.0" - -# ============================================================================ -# Services -# ============================================================================ - -# Rust API service — the primary HTTP/gRPC backend. -# Handles incoming requests, data storage, and core business logic. -[services.rust-api] -image = "ghcr.io/hyperpolymath/myproject-api:latest.ctp" - -# Map host port 8080 to container port 8080. -# Use ["[::]:8080:8080"] for explicit IPv6 binding. -ports = ["8080:8080"] - -# Environment variables passed into the container at startup. -# These override defaults in the Containerfile ENV directives. -environment = { - RUST_LOG = "info", # Rust log level (trace, debug, info, warn, error) - APP_HOST = "[::]", # Listen on all interfaces (IPv4 + IPv6) - APP_PORT = "8080", # Internal container port - APP_LOG_FORMAT = "json", # Structured logging for selur/vordr - APP_DATA_DIR = "/data", # Persistent data directory (matches VOLUME) -} - -# Bind-mount a named volume for persistent data. -# Format: "volume-name:/container/path" -volumes = ["api-data:/data"] - -# Restart policy: "always" ensures the service comes back after crashes. -# Other options: "no", "on-failure", "unless-stopped" -restart = "always" - -# Health check: selur/Podman uses this to determine if the service is ready. -# The service must respond 2xx to this endpoint within the timeout. -healthcheck = { test = "curl -sf http://localhost:8080/health", interval = "30s", timeout = "5s", retries = 3 } - -# --- - -# Elixir worker service — background processing, event handling, coordination. -# Runs as an OTP release with supervision trees for fault tolerance. -[services.elixir-worker] -image = "ghcr.io/hyperpolymath/myproject-worker:latest.ctp" - -# Separate port for the worker's admin/metrics endpoint. -ports = ["4000:4000"] - -# The worker connects to the Rust API over the internal selur network. -# Service names resolve as hostnames within the compose network. -environment = { - API_URL = "http://rust-api:8080/api/v1", # Internal service discovery - MIX_ENV = "prod", # Elixir release mode - APP_LOG_FORMAT = "json", # Match structured logging format - POOL_SIZE = "10", # DB connection pool size -} - -# depends_on ensures the Rust API starts before the worker. -# Note: This only waits for the container to start, not for the health check. -# Use healthcheck + startup probes for true readiness gating. -depends_on = ["rust-api"] - -restart = "always" -healthcheck = { test = "curl -sf http://localhost:4000/health", interval = "30s", timeout = "5s", retries = 3 } - -# --- - -# Svalinn edge gateway — reverse proxy with policy enforcement. -# All external traffic enters through svalinn, which: -# 1. Terminates TLS (auto-provisioned certificates) -# 2. Validates JWT/OAuth2 authentication -# 3. Enforces rate limits from .gatekeeper.yaml -# 4. Routes requests to the appropriate backend service -# 5. Logs all write operations for audit -[services.svalinn] -image = "ghcr.io/hyperpolymath/svalinn:latest.ctp" - -# External-facing ports: HTTPS (443) and HTTP->HTTPS redirect (80). -ports = ["443:443", "80:80"] - -environment = { - # Backend routing: svalinn proxies to internal services. - SVALINN_BACKEND = "http://rust-api:8080", - SVALINN_WORKER_BACKEND = "http://elixir-worker:4000", - - # Policy file: mounted from the svalinn-config volume. - SVALINN_POLICY_FILE = "/etc/svalinn/gatekeeper.yaml", - - # Auto-provision TLS certificates (Let's Encrypt). - SVALINN_TLS_AUTO = "true", -} - -# Mount .gatekeeper.yaml as read-only policy configuration. -volumes = ["svalinn-config:/etc/svalinn:ro"] - -# Svalinn starts last — it needs both backends to be running. -depends_on = ["rust-api", "elixir-worker"] -restart = "always" -healthcheck = { test = "curl -sf http://localhost:80/health", interval = "30s", timeout = "5s", retries = 3 } - -# ============================================================================ -# Volumes -# ============================================================================ - -# Persistent storage for the Rust API (database files, indexes, WAL). -[volumes.api-data] -driver = "local" - -# Read-only policy configuration for svalinn gateway. -# Populate with: cp .gatekeeper.yaml /path/to/svalinn-config/gatekeeper.yaml -[volumes.svalinn-config] -driver = "local" - -# ============================================================================ -# Networks -# ============================================================================ - -# selur network: zero-copy IPC between services on the same host. -# When the selur driver is not installed, falls back to standard bridge -# networking (TCP over localhost). Performance is slightly lower but -# functionality is identical. -[networks.default] -driver = "selur" diff --git a/container/compose.toml b/container/compose.toml deleted file mode 100644 index 1c59e128..00000000 --- a/container/compose.toml +++ /dev/null @@ -1,70 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# -# proven-servers selur-compose configuration -# -# Orchestrates the container stack as verified container bundles (.ctp). -# Uses selur zero-copy IPC between services on the same host. -# -# Usage: -# selur-compose up # Start all services -# selur-compose up --detach # Start in background -# selur-compose verify # Verify all .ctp signatures -# selur-compose ps # Check status -# selur-compose logs -f proven-servers # Stream logs -# selur-compose down # Stop all services -# -# Fallback (when selur is not installed): -# podman compose --file compose.toml up --detach - -version = "1.0" - -# ============================================================================ -# Services -# ============================================================================ - -# Primary application service -[services.proven-servers] -image = "ghcr.io/hyperpolymath/proven-servers:latest.ctp" -ports = ["8080:8080"] -environment = { - APP_HOST = "[::]", - APP_PORT = "8080", - APP_LOG_FORMAT = "json", - APP_DATA_DIR = "/data", -} -volumes = ["proven-servers-data:/data"] -restart = "always" -healthcheck = { test = "curl -sf http://localhost:8080/health", interval = "30s", timeout = "5s", retries = 3 } - -# Svalinn edge gateway: validates requests, enforces policies, TLS termination -[services.svalinn] -image = "ghcr.io/hyperpolymath/svalinn:latest.ctp" -ports = ["443:443", "80:80"] -environment = { - SVALINN_BACKEND = "http://proven-servers:8080", - SVALINN_POLICY_FILE = "/etc/svalinn/gatekeeper.yaml", - SVALINN_TLS_AUTO = "true", -} -volumes = ["svalinn-config:/etc/svalinn:ro"] -depends_on = ["proven-servers"] -restart = "always" -healthcheck = { test = "curl -sf http://localhost:80/health", interval = "30s", timeout = "5s", retries = 3 } - -# ============================================================================ -# Volumes -# ============================================================================ - -[volumes.proven-servers-data] -driver = "local" - -[volumes.svalinn-config] -driver = "local" - -# ============================================================================ -# Networks -# ============================================================================ - -# Use selur zero-copy IPC for inter-service communication on the same host. -# Falls back to standard bridge networking when selur driver is unavailable. -[networks.default] -driver = "selur" diff --git a/container/ct-build.sh b/container/ct-build.sh deleted file mode 100644 index 26f7b2e5..00000000 --- a/container/ct-build.sh +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: MPL-2.0 -# -# proven-servers — Cerro Torre build, sign, and verify pipeline -# -# Builds the container image, packages it as a verified .ctp bundle, -# signs it with Ed25519, and verifies the result. Gracefully degrades -# when cerro-torre tools are not installed. -# -# Prerequisites: -# - podman (container build — required) -# - ct (cerro-torre CLI: pack, sign, verify — optional) -# - cerro-sign (Ed25519 signing — optional, ct sign used as fallback) -# -# Usage: -# ./ct-build.sh # Build + sign (local only) -# ./ct-build.sh --push # Build + sign + push to registry -# CT_KEY_ID=my-key ./ct-build.sh # Use specific signing key -# -# Environment variables: -# CT_KEY_ID — Signing key identifier (default: proven-servers-release) -# CT_REGISTRY — OCI registry to push to (default: ghcr.io/hyperpolymath) -# CT_TAG — Image tag (default: latest) - -set -euo pipefail - -# --------------------------------------------------------------------------- -# Configuration -# --------------------------------------------------------------------------- - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" - -PUSH="" -for arg in "$@"; do - if [ "$arg" = "--push" ]; then - PUSH="--push" - fi -done - -CT_KEY_ID="${CT_KEY_ID:-proven-servers-release}" -CT_REGISTRY="${CT_REGISTRY:-ghcr.io/hyperpolymath}" -CT_TAG="${CT_TAG:-latest}" - -IMAGE_NAME="proven-servers" -FULL_IMAGE="${CT_REGISTRY}/${IMAGE_NAME}:${CT_TAG}" -CTP_FILE="${SCRIPT_DIR}/${IMAGE_NAME}-${CT_TAG}.ctp" - -echo "=== proven-servers Cerro Torre Build Pipeline ===" -echo " Image: ${FULL_IMAGE}" -echo " Key: ${CT_KEY_ID}" -echo " Bundle: ${CTP_FILE}" -echo "" - -# --------------------------------------------------------------------------- -# Step 1: Build container image with Podman -# --------------------------------------------------------------------------- - -echo "--- Step 1: Building container image ---" - -podman build \ - -t "${FULL_IMAGE}" \ - -f "${SCRIPT_DIR}/Containerfile" \ - "${REPO_ROOT}" - -echo " Built: ${FULL_IMAGE}" -echo "" - -# --------------------------------------------------------------------------- -# Step 2: Pack into .ctp bundle -# --------------------------------------------------------------------------- - -echo "--- Step 2: Packing into .ctp bundle ---" - -if command -v ct &>/dev/null; then - ct pack "${FULL_IMAGE}" -o "${CTP_FILE}" - echo " Packed: ${CTP_FILE}" -else - echo " SKIP: ct not found (install cerro-torre CLI from stapeln/container-stack/cerro-torre)" - echo " The container image is built and tagged but not packed as a .ctp bundle." - echo " To pack manually: ct pack ${FULL_IMAGE} -o ${CTP_FILE}" - echo "" - if [ "$PUSH" = "--push" ]; then - echo "--- Pushing unsigned OCI image (no .ctp) ---" - podman push "${FULL_IMAGE}" - echo " Pushed: ${FULL_IMAGE} (unsigned OCI — not a .ctp bundle)" - fi - echo "" - echo "=== Build complete (without .ctp signing) ===" - exit 0 -fi - -echo "" - -# --------------------------------------------------------------------------- -# Step 3: Sign the .ctp bundle -# --------------------------------------------------------------------------- - -echo "--- Step 3: Signing .ctp bundle ---" - -if command -v cerro-sign &>/dev/null; then - cerro-sign sign "${CTP_FILE}" --key-id "${CT_KEY_ID}" - echo " Signed: ${CTP_FILE} (key: ${CT_KEY_ID})" -elif command -v ct &>/dev/null; then - ct sign "${CTP_FILE}" --key "${CT_KEY_ID}" - echo " Signed: ${CTP_FILE} (key: ${CT_KEY_ID})" -else - echo " SKIP: cerro-sign not found (install from stapeln/container-stack/cerro-torre)" -fi - -echo "" - -# --------------------------------------------------------------------------- -# Step 4: Verify the .ctp bundle -# --------------------------------------------------------------------------- - -echo "--- Step 4: Verifying .ctp bundle ---" - -if command -v ct &>/dev/null; then - ct verify "${CTP_FILE}" - echo " Verified: ${CTP_FILE}" -else - echo " SKIP: ct not found" -fi - -echo "" - -# --------------------------------------------------------------------------- -# Step 5: Push to registry (optional) -# --------------------------------------------------------------------------- - -if [ "$PUSH" = "--push" ]; then - echo "--- Step 5: Pushing to registry ---" - - if command -v ct &>/dev/null; then - ct push "${CTP_FILE}" "${FULL_IMAGE}" - echo " Pushed: ${FULL_IMAGE}" - else - # Fall back to podman push (unsigned OCI image) - echo " ct not available, falling back to podman push (unsigned)" - podman push "${FULL_IMAGE}" - echo " Pushed: ${FULL_IMAGE} (unsigned OCI — not a .ctp bundle)" - fi - echo "" -fi - -# --------------------------------------------------------------------------- -# Summary -# --------------------------------------------------------------------------- - -echo "=== Build pipeline complete ===" -echo " Image: ${FULL_IMAGE}" -echo " Bundle: ${CTP_FILE}" -echo "" -echo " To deploy with selur-compose:" -echo " cd container && selur-compose up" -echo "" -echo " To verify at any time:" -echo " ct verify ${CTP_FILE}" -echo "" -echo " To explain the verification chain:" -echo " ct explain ${CTP_FILE}" diff --git a/container/deploy.k9.ncl b/container/deploy.k9.ncl deleted file mode 100644 index dd72b138..00000000 --- a/container/deploy.k9.ncl +++ /dev/null @@ -1,176 +0,0 @@ -K9! -# SPDX-License-Identifier: MPL-2.0 -# deploy.k9.ncl — proven-servers deployment component (Hunt level) -# -# k9-svc deployment specification with full pedigree (L1-L5). -# Security Level: 'hunt (requires cryptographic handshake for execution). -# -# WARNING: This component can execute shell commands! -# It requires explicit authorisation via the Leash system. -# -# Usage: -# nickel typecheck container/deploy.k9.ncl -# k9-svc validate container/deploy.k9.ncl -# k9-svc deploy container/deploy.k9.ncl --env production - -# The component's pedigree (self-description across five layers) -let component_pedigree = { - # ───────────────────────────────────────────────────────────── - # L1: The Snout — Identity - # ───────────────────────────────────────────────────────────── - metadata = { - name = "proven-servers-deploy", - version = "0.1.0", - breed = "application/vnd.k9+nickel", - magic_number = "K9!", - description = "proven-servers deployment component (Hunt level)", - }, - - # ───────────────────────────────────────────────────────────── - # L2: The Scent — Target Environment - # ───────────────────────────────────────────────────────────── - target = { - os = 'Linux, - is_edge = false, - requires_podman = true, - min_memory_mb = 256, - }, - - # ───────────────────────────────────────────────────────────── - # L3: The Leash — Security - # ───────────────────────────────────────────────────────────── - security = { - trust_level = "hunt", - allow_network = true, - allow_filesystem_write = true, - allow_subprocess = true, - # In production, replace with a real Ed25519 signature. - signature = "PLACEHOLDER-SIGNATURE-REQUIRED-FOR-HUNT", - }, - - # ───────────────────────────────────────────────────────────── - # L4: The Gut — Self-Validation - # ───────────────────────────────────────────────────────────── - validation = { - checksum = "sha256:placeholder", - pedigree_version = "1.0.0", - hunt_authorized = false, # Must be set true after handshake - }, - - # ───────────────────────────────────────────────────────────── - # L5: The Muscle — Deployment Recipes - # ───────────────────────────────────────────────────────────── - recipes = { - install = "just container-build", - validate = "just container-verify", - deploy = "just container-up", - migrate = "just container-build && just container-up", - }, -} in - -# Deployment configuration -let deployment_def = { - # Target environments (dev / staging / production) - environments = { - dev = { - replicas = 1, - memory = "256Mi", - cpu = "100m", - image_tag = "dev", - }, - staging = { - replicas = 2, - memory = "512Mi", - cpu = "250m", - image_tag = "staging", - }, - production = { - replicas = 3, - memory = "1Gi", - cpu = "500m", - image_tag = "latest", - }, - }, - - # Container configuration - container = { - image = "ghcr.io/hyperpolymath/proven-servers", - port = 8080, - health_check = "/health", - readiness_check = "/ready", - }, - - # Deployment strategy - strategy = { - type = "rolling", - max_surge = 1, - max_unavailable = 0, - }, -} in - -# Deployment scripts (executed at Hunt level) -let scripts_def = { - # Pre-deployment validation - pre_deploy = m%" -#!/bin/sh -set -eu -echo "K9: Pre-deployment validation for proven-servers..." -cd container && selur-compose verify || podman compose --file compose.toml config -echo "K9: Validation passed." -"%, - - # Deployment script - deploy = m%" -#!/bin/sh -set -eu -ENV="${1:-dev}" -echo "K9: Deploying proven-servers to $ENV environment..." -cd container -./ct-build.sh -selur-compose up --detach || podman compose --file compose.toml up --detach -echo "K9: Deployment to $ENV complete." -"%, - - # Rollback script - rollback = m%" -#!/bin/sh -set -eu -echo "K9: Rolling back proven-servers deployment..." -cd container -selur-compose down || podman compose --file compose.toml down -echo "K9: Rollback complete." -"%, -} in - -# Export the component -{ - # Validator-visible identity block (k9-validate-action tracks the brace - # block after `pedigree =`); merged with the full five-layer pedigree. - pedigree = { - name = component_pedigree.metadata.name, - version = component_pedigree.metadata.version, - # Literal tier: k9-validate-action text-parses this line and cannot - # resolve a Nickel reference, so the leash must be a bare "kennel"/"yard"/ - # "hunt" string here (it mirrors security.trust_level above). - leash = "hunt", - } & component_pedigree, - deployment = deployment_def, - scripts = scripts_def, - - # Security check: this component requires Hunt level - required_level = 'hunt, - - # Warning for users - warning = m%" -WARNING: This is a Hunt-level component. - -It can execute shell commands and modify your system. -Before running, ensure you have: - -1. Reviewed the deployment scripts above -2. Verified the signature (when implemented) -3. Explicitly authorised Hunt-level execution - -Run with: k9-svc authorize container/deploy.k9.ncl && k9-svc deploy container/deploy.k9.ncl -"%, -} diff --git a/container/entrypoint.sh b/container/entrypoint.sh deleted file mode 100644 index a8968ab4..00000000 --- a/container/entrypoint.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: MPL-2.0 -# proven-servers container entrypoint -# -# Handles signal propagation, startup logging, and health check -# preparation before exec-ing into the main application process. - -set -e - -# --------------------------------------------------------------------------- -# Signal handling -# --------------------------------------------------------------------------- -# -# Trap SIGTERM and SIGINT so that the application can shut down gracefully -# when Podman sends stop signals (e.g. `podman stop`, `selur-compose down`). - -cleanup() { - echo "Received shutdown signal — stopping proven-servers..." - # If the main process is backgrounded, kill it here: - # kill "$MAIN_PID" 2>/dev/null || true - # wait "$MAIN_PID" 2>/dev/null || true - exit 0 -} -trap cleanup TERM INT - -# --------------------------------------------------------------------------- -# Startup logging -# --------------------------------------------------------------------------- - -echo "Starting proven-servers..." -echo " Host: ${APP_HOST:-[::]}" -echo " Port: ${APP_PORT:-8080}" -echo " Data: ${APP_DATA_DIR:-/data}" -echo " Log: ${APP_LOG_FORMAT:-json}" - -# --------------------------------------------------------------------------- -# Health check preparation -# --------------------------------------------------------------------------- -# -# Ensure the data directory exists and is writable. -# The VOLUME directive in the Containerfile creates /data, but a bind-mount -# might replace it with an empty directory owned by root. - -if [ -d "${APP_DATA_DIR:-/data}" ]; then - if [ ! -w "${APP_DATA_DIR:-/data}" ]; then - echo "WARNING: ${APP_DATA_DIR:-/data} is not writable by $(whoami)" - fi -fi - -# --------------------------------------------------------------------------- -# Exec into main process -# --------------------------------------------------------------------------- -# -# Replace the entrypoint shell with the application process so that -# signals are delivered directly and PID 1 is the application. -# -# TODO: Replace the command below with your application binary. -# Examples: -# exec /app/proven-servers -# exec /app/release/bin/proven-servers start -# exec /app/proven-servers serve --host "${APP_HOST}" --port "${APP_PORT}" - -exec "$@" diff --git a/container/manifest.toml b/container/manifest.toml deleted file mode 100644 index 54c3eba6..00000000 --- a/container/manifest.toml +++ /dev/null @@ -1,62 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# -# Cerro Torre manifest for proven-servers .ctp bundle -# -# This manifest describes the container image for verified -# container packaging. Used by `ct pack` to create .ctp bundles. - -[metadata] -name = "proven-servers" -version = "0.1.0" -revision = 1 -summary = "Formally verified server components in Idris 2 with Zig FFI" -description = """ -proven-servers — containerised service packaged as a verified -cerro-torre .ctp bundle with Ed25519 signing and full provenance -tracking. -""" -license = "MPL-2.0" -homepage = "https://github.com/hyperpolymath/proven-servers" -maintainer = "Jonathan D.A. Jewell " - -[provenance] -upstream = "https://github.com/hyperpolymath/proven-servers" -import_date = 2026-03-02T00:00:00Z - -[dependencies] -runtime = ["ca-certificates", "curl"] -build = [] - -[build] -system = "podman" - -[build.environment] -APP_HOST = "[::]" -APP_PORT = "8080" - -[outputs] -primary = "proven-servers" -split = [] - -[attestations] -require = ["source-signature", "sbom-complete"] -recommend = ["security-audit", "reproducible-build"] - -# Runtime security profile -[security] -user = "appuser" -group = "appuser" -read_only_root = false -no_new_privileges = true - -[security.capabilities] -drop = ["ALL"] -add = ["NET_BIND_SERVICE"] - -[security.network] -listen_tcp = [8080] - -[security.filesystem] -read = ["/app/", "/data/"] -write = ["/data/", "/tmp/"] -execute = ["/app/entrypoint.sh"] diff --git a/container/vordr.toml b/container/vordr.toml deleted file mode 100644 index e613dbed..00000000 --- a/container/vordr.toml +++ /dev/null @@ -1,100 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# -# Vordr runtime monitoring configuration for proven-servers -# -# Vordr watches container health, detects crashes, tracks resource usage, -# and emits structured logs. It runs alongside the application stack and -# provides runtime observability without requiring in-process agents. -# -# Usage: -# vordr watch --config container/vordr.toml -# vordr status -# vordr report - -[metadata] -name = "proven-servers" -version = "0.1.0" - -# ============================================================================ -# Health Monitoring -# ============================================================================ -# -# Vordr periodically probes these endpoints. If a probe fails beyond the -# failure_threshold, vordr emits an alert and (optionally) restarts the -# container via Podman. - -[health] -# Primary health endpoint — must return 2xx. -endpoint = "http://localhost:8080/health" -interval = "30s" -timeout = "5s" -failure_threshold = 3 - -# Readiness endpoint — checked during startup and after restarts. -readiness_endpoint = "http://localhost:8080/ready" -readiness_timeout = "10s" - -# Action on failure: "alert" (log + notify) or "restart" (alert + podman restart). -on_failure = "alert" - -# ============================================================================ -# Crash Detection -# ============================================================================ -# -# Monitors container state via Podman. Detects OOM kills, segfaults, -# and unexpected exits. - -[crash_detection] -enabled = true -# Maximum restarts within the window before vordr stops restarting. -max_restarts = 5 -restart_window = "10m" - -# ============================================================================ -# Resource Thresholds -# ============================================================================ -# -# Alert when resource usage exceeds these thresholds. Values are percentages -# of the container's cgroup limits (or host limits if uncapped). - -[resources] -cpu_warn = 80 # Percentage — warn at 80% sustained CPU. -cpu_critical = 95 # Percentage — critical alert at 95%. -memory_warn = 75 # Percentage of memory limit. -memory_critical = 90 -disk_warn = 80 # Percentage of volume usage. -disk_critical = 95 - -# Sample interval for resource metrics. -sample_interval = "15s" - -# ============================================================================ -# Log Output -# ============================================================================ -# -# Vordr emits its own logs (not the application's) in structured format. - -[logging] -format = "json" -level = "info" -# Write vordr logs to stdout (captured by Podman) and optionally to file. -output = "stdout" -# file = "/var/log/vordr/proven-servers.log" - -# ============================================================================ -# Notifications (optional) -# ============================================================================ -# -# Uncomment and configure to receive alerts via webhook or email. - -# [notifications.webhook] -# url = "https://example.com/hooks/vordr" -# method = "POST" -# headers = { "Content-Type" = "application/json" } -# on = ["failure", "recovery", "resource_critical"] - -# [notifications.email] -# to = "j.d.a.jewell@open.ac.uk" -# from = "vordr@proven-servers.local" -# smtp = "smtp://localhost:25" -# on = ["failure", "resource_critical"] diff --git a/contractile.just b/contractile.just index 8e3aa5b3..cac2cab8 100644 --- a/contractile.just +++ b/contractile.just @@ -1,115 +1,23 @@ -# Auto-generated by: contractile gen-just -# Source directory: contractiles -# Re-generate with: contractile gen-just --dir contractiles -# # SPDX-License-Identifier: MPL-2.0 - -# === DUST (Recovery & Rollback) === -# Source: Dustfile.a2ml - -# List available dust recovery actions +# Repository-specific, non-destructive contractile task wrappers. +# The generated template helpers formerly checked nonexistent files and exposed +# a `git checkout HEAD -- .` rollback. They have been replaced with bounded +# checks that do not discard working-tree changes. + +must-check: validate + bash tools/check-binding-policy.sh + test -f SECURITY.adoc + test -f .well-known/security.txt + +# Source-pattern security checks only; not cryptographic/integrity verification. +trust-smoke: + bash tests/aspect/security_test.sh + bash tools/check-binding-policy.sh + +# Show current worktree state; no automatic rollback is provided. dust-status: - @echo ' dust-source-rollback: Revert all source changes to last commit [rollback]' - -# Revert all source changes to last commit -dust-source-rollback: - @echo 'Executing rollback for source-rollback' - git checkout HEAD -- . - + @git status --short -# === INTEND (Declared Future Intent) === -# Source: Intentfile.a2ml - -# Display declared future intents +# Display the declared near-term work assembly for human review. intend-list: - @echo '=== Declared Intent ===' - @echo '' - @echo 'ABI-FFI:' - @echo '' - @echo 'Servers:' - @echo '' - @echo 'Verification:' - @echo '' - @echo 'Quality:' - - -# === MUST (Physical State Checks) === -# Source: Mustfile.a2ml - -# Run all must checks -must-check: must-license-present must-readme-present must-security-md must-containerfile-present must-ai-manifest must-idris2-abi-exists must-zig-ffi-exists must-generated-headers must-no-dangerous-patterns must-spdx-headers must-no-dockerfile must-no-makefile must-no-env-files - @echo 'All must checks passed' - -# LICENSE file must exist -must-license-present: - test -f LICENSE - -# README must exist -must-readme-present: - test -f README.adoc || test -f README.md - -# SECURITY.md must exist -must-security-md: - test -f SECURITY.md - -# Containerfile must exist -must-containerfile-present: - test -f Containerfile - -# AI manifest must exist -must-ai-manifest: - test -f 0-AI-MANIFEST.a2ml || test -f AI.a2ml - -# Idris2 ABI definitions must exist in src/abi/ -must-idris2-abi-exists: - test -f src/abi/Types.idr && test -f src/abi/Layout.idr && test -f src/abi/Foreign.idr - -# Zig FFI implementation must exist -must-zig-ffi-exists: - test -f ffi/zig/build.zig && test -f ffi/zig/src/main.zig - -# Generated C headers directory should exist -must-generated-headers: - test -d generated/abi || test -d abi - -# No believe_me, assert_total, or similar unsafe patterns -must-no-dangerous-patterns: - grep -rn 'believe_me\|assert_total\|unsafeCoerce\|Admitted\|sorry' src/ --include='*.idr' --include='*.rs' | wc -l | grep -q '^0$' - -# Source files should have SPDX headers -must-spdx-headers: - find src/ -name '*.idr' -o -name '*.rs' -o -name '*.zig' | head -20 | xargs -r grep -L 'SPDX-License-Identifier' | wc -l | grep -q '^0$' - -# No Dockerfiles (use Containerfile) -must-no-dockerfile: - test ! -f Dockerfile - -# No Makefiles (use Justfile) -must-no-makefile: - test ! -f Makefile - -# No .env files committed -must-no-env-files: - test ! -f .env - - -# === TRUST (Integrity & Provenance Verification) === -# Source: Trustfile.a2ml - -# Run all trust verifications -trust-verify: trust-license-content trust-no-secrets-committed trust-container-images-pinned - @echo 'All trust verifications passed' - -# LICENSE contains expected SPDX identifier -trust-license-content: - grep -q 'SPDX\|License\|MIT\|Apache\|PMPL\|MPL' LICENSE - -# No .env or credential files in repo -trust-no-secrets-committed: - test ! -f .env && test ! -f credentials.json && test ! -f .env.local - -# Containerfile base images use pinned digests -trust-container-images-pinned: - test ! -f Containerfile || grep -q '@sha256:' Containerfile - - + @cat .machine_readable/contractiles/Intentfile.a2ml diff --git a/docs/AI-CONVENTIONS.adoc b/docs/AI-CONVENTIONS.adoc index 980985e2..3837c3c4 100644 --- a/docs/AI-CONVENTIONS.adoc +++ b/docs/AI-CONVENTIONS.adoc @@ -1,99 +1,67 @@ -== AI Conventions (Authoritative Source) - -All AI coding agents working in this repository MUST follow these rules. -Per-tool config files (.cursorrules, .clinerules, etc.) reference this -document. - -=== Session Startup - -[arabic] -. Read `+0-AI-MANIFEST.a2ml+` FIRST (mandatory gatekeeper). -. Read `+.machine_readable/STATE.a2ml+` for current status and blockers. -. Read `+.machine_readable/anchors/ANCHOR.a2ml+` for canonical authority -boundaries. -. Read `+.machine_readable/policies/MAINTENANCE-AXES.a2ml+` for -maintenance/audit sequencing. -. Read `+.machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml+` for -baseline controls. -. Read `+.machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml+` -for execution order. -. Read `+.machine_readable/AGENTIC.a2ml+` for agent constraints. - -=== License - -* All original code: *MPL-2.0* -* Fallback (platform-required only): MPL-2.0 with comment explaining -why. -* NEVER use AGPL-3.0. -* Preserve third-party licenses verbatim. -* Every source file needs `+# SPDX-License-Identifier: CC-BY-SA-4.0+`. - -=== Author Attribution - -* Name: *Jonathan D.A. Jewell* -* Email: *j.d.a.jewell@open.ac.uk* -* Copyright: -`+Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +` - -=== State Files - -State/metadata files, anchors, and policies (.a2ml) belong in -`+.machine_readable/+` ONLY. NEVER create STATE.a2ml, META.a2ml, -ECOSYSTEM.a2ml, AGENTIC.a2ml, NEUROSYM.a2ml, PLAYBOOK.a2ml, ANCHOR.a2ml, -MAINTENANCE-AXES.a2ml, MAINTENANCE-CHECKLIST.a2ml, or -SOFTWARE-DEVELOPMENT-APPROACH.a2ml in the repository root. - -=== Banned Patterns - -[width="100%",cols="14%,50%,36%",options="header",] -|=== -|Language |Banned |Reason -|Idris2 |`+believe_me+`, `+assert_total+` |Unsound escape hatches -|Haskell |`+unsafeCoerce+`, `+unsafePerformIO+` |Breaks type safety -|OCaml |`+Obj.magic+`, `+Obj.repr+`, `+Obj.obj+` |Unsafe casting -|Coq |`+Admitted+` |Unproven assumption -|Lean |`+sorry+` |Unproven assumption -|Rust |`+transmute+` (unless FFI + SAFETY:) |Unsound reinterpret -|=== - -=== Banned Languages - -[cols=",",options="header",] -|=== -|Banned |Use Instead -|TypeScript |ReScript -|Node.js / npm / bun |Deno -|Go |Rust -|Python |Julia / Rust -|=== - -=== Container Standard - -* Runtime: *Podman* (never Docker). -* File: *Containerfile* (never Dockerfile). -* Base images: `+cgr.dev/chainguard/wolfi-base:latest+` or -`+cgr.dev/chainguard/static:latest+`. - -=== ABI/FFI Standard - -* ABI definitions: *Idris2* with dependent types (`+src/abi/+`). -* FFI implementation: *Zig* with C ABI compatibility (`+ffi/zig/+`). -* Generated C headers: `+generated/abi/+`. - -=== Build System - -Use `+just+` (Justfile) for all build, test, lint, and format tasks. - -=== References - -* `+0-AI-MANIFEST.a2ml+` – universal AI entry point -* `+.machine_readable/AGENTIC.a2ml+` – agent permissions and constraints -* `+.machine_readable/STATE.a2ml+` – current project state -* `+.machine_readable/anchors/ANCHOR.a2ml+` – canonical authority and -policy boundary -* `+.machine_readable/policies/MAINTENANCE-AXES.a2ml+` – canonical axis -sequencing and audit requirements -* `+.machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml+` – baseline -maintenance checklist policy -* `+.machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml+` – -axis execution approach policy +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) 2026 Jonathan D.A. Jewell += AI Conventions for proven-servers +:toc: + +These conventions apply to AI-assisted changes in this repository. They are +project guidance, not a substitute for package-level documentation, executed +checks, or maintainer review. + +== Session Startup + +. Read `0-AI-MANIFEST.a2ml`. +. Read `.machine_readable/6a2/STATE.a2ml` and + `.machine_readable/6a2/anchor/ANCHOR.a2ml`. +. Read `.machine_readable/policies/MAINTENANCE-AXES.a2ml` and the package-local + README for the code being changed. +. Inspect the current diff and run relevant checks; do not inherit historical + build/test results as evidence for modified sources. + +== Evidence and Scope + +* Distinguish Idris2 model checks from Zig implementation tests, generated ABI + checks, binding link tests, and protocol interoperability tests. +* Treat shell smoke checks as source-pattern heuristics only. +* Never claim a language binding is supported from directory presence or + declarations alone; update `.machine_readable/BINDINGS.a2ml` only when current + build/link/runtime evidence supports the status. +* Keep authentication, cryptographic, and other security-sensitive operations + fail-closed when required credentials, key material, challenge bytes, or + reviewed backends are absent. +* Container and deployment recipes are disabled until this source monorepo has a + real executable service target and verified runtime image. +* Report missing tools and skipped checks explicitly. Re-run the relevant check + after changing its implementation or test harness. + +== Languages and FFI + +Idris2 appears in selected model/ABI packages; Zig appears in selected native +FFI packages. The existing `bindings/` tree contains sources in many languages, +but that inventory does not imply operational support. There is no blanket ban +on languages already used in this repository; document scope and toolchain +rationale before introducing another runtime or language. + +Never use known proof escape hatches such as `believe_me`, `assert_total`, +`unsafeCoerce`, `Obj.magic`, `Admitted`, or `sorry` to support a verification +claim. Source scans are heuristic and do not establish the absence of all +unsound assumptions. + +== Licensing and Attribution + +* Preserve file-level SPDX identifiers and third-party notices. +* Original project source is generally MPL-2.0; follow the existing file and + directory license policy rather than mass-changing headers. +* Copyright attribution, where required, uses Jonathan D.A. Jewell and + `j.d.a.jewell@open.ac.uk`. + +== Configured Commands + +* `just test-static` — source-pattern and inventory smoke checks. +* `just build-idris` — discovered Idris2 packages; requires `idris2`. +* `just build-zig` / `just test-zig` — discovered Zig packages; require `zig`. +* `just quality` — configured checks including compiler-backed tasks; requires + Just, Idris2, and Zig. +* `just fmt-check` — Git whitespace checks only; it is not a formatter. + +No repository-wide language formatter, full binding conformance matrix, or +production release/deployment workflow is currently configured. diff --git a/docs/AI-INSTALL-README-SECTION.adoc b/docs/AI-INSTALL-README-SECTION.adoc index af610e0a..6813f289 100644 --- a/docs/AI-INSTALL-README-SECTION.adoc +++ b/docs/AI-INSTALL-README-SECTION.adoc @@ -1,117 +1,14 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) -// -// ============================================================================ -// AI-NATIVE INSTALLATION SECTION — paste into your README.adoc -// -// Place this section AFTER badges and BEFORE your first content section -// (e.g., before "== Overview" or "== What This Is"). -// -// After pasting, fill in every [TODO-AI-INSTALL] marker. -// finishbot checks these are complete before release. -// -// Then fill in the full recipe in docs/AI_INSTALLATION_GUIDE.adoc -// ============================================================================ - -== AI-Assisted Installation (Recommended) - -=== Just Say It - -**You don't need to read this README.** Just say this to any AI assistant: - -[source,text] ----- -Set up proven-servers from https://github.com/hyperpolymath/proven-servers ----- - -**The URL is the spec.** The AI fetches this repo, reads the installation guide inside it, and handles everything. You answer a few questions and confirm the privacy notice. No commands to type, no installers to figure out, no docs to read. - -Any AI that can read a URL and run commands (or generate commands for you to paste) can do this. The guide inside the repo tells the AI everything. - -The AI handles: - -// [TODO-AI-INSTALL] Replace this list with what your project's AI setup actually does. -// Examples from real repos: -// neurophone: "Installing Termux, Rust, building for your hardware, downloading the LLM model" -// feedback-o-tron: "Installing Elixir, building the CLI, walking through credential setup" -// Delete this comment block and replace: - -* [TODO-AI-INSTALL: What the AI does during setup -- 4-6 bullet points] - -=== Other Ways to Say It - -If your AI already knows about proven-servers (web search, training data), shorter versions work: - -// [TODO-AI-INSTALL] Replace with natural-language variations specific to your project. -// Delete this comment block and replace: - -* "[TODO-AI-INSTALL: Short version without URL]" -* "[TODO-AI-INSTALL: Version mentioning specific use case]" - -If it doesn't know the project, just include the URL: - -* "Set up https://github.com/hyperpolymath/proven-servers on my machine" - -=== What You'll Be Asked - -Your AI will ask you: - -// [TODO-AI-INSTALL] Same questions as in the AI_INSTALLATION_GUIDE.adoc. -// Delete this comment block and replace: - -1. **[TODO-AI-INSTALL: Question 1]** -2. **[TODO-AI-INSTALL: Question 2]** -3. **Privacy confirmation** -- what the tool does and how your data is handled - -That's it. Everything else is automatic. - -=== Privacy & Security Notice - -[IMPORTANT] -==== -// [TODO-AI-INSTALL] Same privacy notice as in the AI_INSTALLATION_GUIDE.adoc. -// Delete this comment block and replace: - -**What proven-servers does:** - -* [TODO-AI-INSTALL: What it does] - -**What proven-servers does NOT do:** - -* [TODO-AI-INSTALL: What it does NOT do] - -**You control everything.** Uninstall anytime. -==== - -=== After Install - -// [TODO-AI-INSTALL] Brief post-install usage example. -// Delete this comment block and replace: - -[source,bash] ----- -# [TODO-AI-INSTALL: Example first command after install] ----- - -=== Uninstall - -Tell your AI: "Remove proven-servers from my system" - -=== Troubleshooting - -// [TODO-AI-INSTALL] 3-5 most common issues. Keep brief -- the AI -// can read the full troubleshooting in the installation guide. -// Delete this comment block and replace: - -[cols="1,3"] -|=== -|Problem |Solution - -|[TODO-AI-INSTALL] -|[TODO-AI-INSTALL] -|=== - -For manual installation without AI assistance, see the <> section below. - -''' += AI-Assisted Repository Orientation + +This repository has no end-user application to install or run. It contains +research/prototype source, protocol models, FFI experiments, and binding +scaffolds; it is not a production server distribution. + +If you want to inspect or contribute to the source, read +`QUICKSTART-USER.adoc` or `QUICKSTART-DEV.adoc`. Do not ask an AI assistant to +install packages, run a service, change system settings, or deploy resources +from this repository without explicit review and authorization. The current +scope and limitations are recorded in `READINESS.adoc` and +`PROOF-NEEDS.adoc`. diff --git a/docs/AI_INSTALLATION_GUIDE.adoc b/docs/AI_INSTALLATION_GUIDE.adoc index 4ba7f45a..cc1fafbd 100644 --- a/docs/AI_INSTALLATION_GUIDE.adoc +++ b/docs/AI_INSTALLATION_GUIDE.adoc @@ -1,299 +1,74 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) -= proven-servers: AI-Assisted Installation Guide += AI-Assisted Repository Orientation — proven-servers :toc: -:toc-placement: preamble - -// ============================================================================ -// TEMPLATE INSTRUCTIONS (delete this block after completing) -// -// This is the AI-native installation guide template. It contains everything -// an AI assistant needs to install your project for a user who says one sentence. -// -// BEFORE RELEASE, you or your AI must fill in every section marked [TODO]. -// finishbot checks that no [TODO-AI-INSTALL] markers remain. -// -// The pattern: -// 1. User says "set up proven-servers from https://github.com/hyperpolymath/proven-servers" -// 2. AI fetches this repo, reads THIS file -// 3. AI follows the recipe in "For AI Assistants" section -// 4. User answers 2-3 questions, confirms privacy notice -// 5. Done -- no manual steps, no forms, no docs to read -// -// Fill in every [TODO-AI-INSTALL] section. Be specific -- the AI executing -// this will take your instructions literally. Include exact commands, exact -// URLs for credential creation, exact config file contents. -// -// Reference implementations: -// - neurophone/docs/AI_INSTALLATION_GUIDE.adoc (Android/Rust/Termux) -// - feedback-o-tron/docs/AI_INSTALLATION_GUIDE.adoc (Elixir CLI/MCP) -// ============================================================================ - -[NOTE] -==== -**If you are an AI assistant reading this:** A user has asked you to install proven-servers. Skip to <> for the complete step-by-step recipe. You have everything you need in this document -- no other docs required. Follow the steps in order. -==== - -== For Users: How to Use This Feature - -=== Just Say It - -Open any AI assistant (Claude, ChatGPT, Gemini, or any other) and say: - ----- -Set up proven-servers from https://github.com/hyperpolymath/proven-servers ----- - -The AI reads this repo and handles everything. You answer a few questions and confirm the privacy notice. No commands to type, no installers to figure out, no docs to read. - -=== What You'll Be Asked - -Your AI will ask you: - -// [TODO-AI-INSTALL] List the 2-4 questions the AI needs answered. -// Keep this minimal -- only things the AI genuinely cannot determine on its own. -// Examples: -// - "What platform/OS are you on?" (if multi-platform) -// - "Which backends do you want?" (if modular) -// - "Do you have an API key for X?" (if credentials needed) -// -// Delete this comment block and replace with your questions: - -1. **[TODO-AI-INSTALL: Question 1]** -2. **[TODO-AI-INSTALL: Question 2]** -3. **Privacy confirmation** -- what the tool does and how your data is handled - -That's it. Everything else is automatic. - -=== Privacy & Security Notice +:sectnums: [IMPORTANT] ==== -// [TODO-AI-INSTALL] Fill in what your project actually does with user data. -// Be specific and honest. This is shown to the user before installation. -// Delete this comment block and replace with your privacy notice: - -**What proven-servers does:** - -* [TODO-AI-INSTALL: What data/resources does it access?] -* [TODO-AI-INSTALL: Where does it store data?] -* [TODO-AI-INSTALL: What network calls does it make?] - -**What proven-servers does NOT do:** - -* [TODO-AI-INSTALL: What privacy guarantees can you make?] - -**You control everything.** Uninstall anytime. +There is no end-user application to install. `proven-servers` is a research +source monorepo, not a production server distribution. Do not install, +deploy, publish, or configure a service from these sources on a user's behalf. ==== -=== After Install +== If a user asks to install or run proven-servers -// [TODO-AI-INSTALL] Show what the user sees after successful installation. -// Include example output, first command to try, etc. -// Delete this comment block and replace with post-install info: +Explain that this repository currently provides source, models, tests, and +experimental FFI/binding code rather than a supported service. Do not imply +that cloning the repository installs an application. -[source,bash] ----- -# [TODO-AI-INSTALL: Example first command after install] ----- +If the user wants to explore or contribute, point them to +`QUICKSTART-USER.adoc` or `QUICKSTART-DEV.adoc`. The latter documents the +actual Just recipes and compiler requirements. Do not invent commands such as +`just setup`, `just run`, or `just install`; they are not configured tasks. -=== Uninstall +== Non-mutating orientation -Tell your AI: "Remove proven-servers from my system" +From an existing checkout, a maintainer may inspect status with: -// [TODO-AI-INSTALL] Provide manual uninstall commands as fallback: [source,bash] ---- -# [TODO-AI-INSTALL: Manual uninstall commands] ----- - -[[ai-implementation]] -== For AI Assistants: Implementation Guide - -// ============================================================================ -// THIS IS THE RECIPE YOUR AI FOLLOWS. -// Be extremely specific. Include exact commands, exact URLs, exact file -// contents. The AI executing this has never seen your project before. -// ============================================================================ - -=== When User Asks to Install proven-servers - -==== Step 1: Determine Setup - -Ask the user: - -// [TODO-AI-INSTALL] Write the exact questions the AI should ask. -// Format as a copy-pasteable block the AI can show the user. -// Delete this comment block and replace: - +just info +just doctor +just --list ---- -I can help you set up proven-servers. Before we start: -1. [TODO-AI-INSTALL: Question for AI to ask] -2. [TODO-AI-INSTALL: Question for AI to ask] ----- +`just doctor` reports local tool availability and Git state; it does not install +or repair anything. `setup.sh` is also a non-mutating information helper and +never downloads scripts, installs packages, changes firewall/SELinux settings, +or deploys resources. -==== Step 2: Check Prerequisites +== Optional developer checks -// [TODO-AI-INSTALL] List every prerequisite with version requirements -// and the exact commands to check/install them. -// Delete this comment block and replace: +When the user explicitly wants to build source and has installed the tools +through their approved package-management process: [source,bash] ---- -# Check prerequisites -# [TODO-AI-INSTALL: Exact version check commands] -# Example: -# elixir --version # Need 1.15+ -# rustc --version # Need 1.75+ -# git --version # Any recent version ----- - -If prerequisites are missing, install them: - -// [TODO-AI-INSTALL] Provide install commands per OS/platform. -// Cover at minimum: Fedora, Ubuntu/Debian, macOS, asdf. -// Delete this comment block and replace: - ----- -# Fedora/RHEL -# [TODO-AI-INSTALL: dnf install commands] - -# Ubuntu/Debian -# [TODO-AI-INSTALL: apt install commands] - -# macOS -# [TODO-AI-INSTALL: brew install commands] - -# asdf (cross-platform) -# [TODO-AI-INSTALL: asdf install commands] ----- - -==== Step 3: Present Privacy Notice - -**ALWAYS show this BEFORE any installation commands. Wait for explicit "yes".** - +just deps +just test-static +just build-idris +just build-zig +just test-zig ---- -proven-servers PRIVACY & SECURITY NOTICE -proven-servers will: -- [TODO-AI-INSTALL: What it does -- data access, network, storage] +`just deps` checks availability only. Missing compilers cause the build/test +tasks to fail. Static checks are source-pattern and inventory heuristics, not +proof, security certification, or protocol conformance. Read package-specific +instructions and record the exact tool versions and outcomes. -proven-servers will NOT: -- [TODO-AI-INSTALL: What it does NOT do -- privacy guarantees] - -[TODO-AI-INSTALL: Storage locations, credential handling, audit logging] - -Do you understand and accept? (yes/no) ----- - -==== Step 4: Install - -// [TODO-AI-INSTALL] The COMPLETE installation command block. -// This should be a single copy-paste block that does everything. -// Include: clone, build, config creation, model downloads, etc. -// Delete this comment block and replace: - -[source,bash] ----- -# proven-servers Installation -# [TODO-AI-INSTALL: Complete installation commands] -# -# Example structure: -# git clone https://github.com/hyperpolymath/proven-servers -# cd proven-servers -# [build commands] -# [config creation] -# [any downloads] -# [verification command] ----- - -==== Step 5: Configure Credentials (if applicable) - -// [TODO-AI-INSTALL] If your project needs API keys, tokens, or other -// credentials, provide EXACT instructions for each service: -// - URL to create the credential -// - What scopes/permissions are needed -// - The exact export command -// If no credentials needed, delete this section. -// Delete this comment block and replace: - -===== [TODO-AI-INSTALL: Service Name] - ----- -To create a [credential type]: - -1. Go to: [TODO-AI-INSTALL: Exact URL] -2. [TODO-AI-INSTALL: Step-by-step] -3. Copy the token - -Then run: -export [TODO-AI-INSTALL: ENV_VAR]=[value] ----- - -==== Step 6: Configure Integration (if applicable) - -// [TODO-AI-INSTALL] If your project integrates with other tools -// (MCP server for Claude Code, function definitions for ChatGPT, etc.) -// provide the exact configuration. -// If no integration needed, delete this section. - -==== Step 7: Verify Installation - -// [TODO-AI-INSTALL] A safe verification command (dry-run or status check) -// that proves everything is working. Include expected output. -// Delete this comment block and replace: - -[source,bash] ----- -# [TODO-AI-INSTALL: Verification command] ----- - -Expected output: - ----- -[TODO-AI-INSTALL: What the user should see] ----- - -==== Step 8: Show Usage - -After verification, show the user: - ----- -Setup complete! Here's how to use proven-servers: - -[TODO-AI-INSTALL: 3-5 example commands covering main use cases] ----- - -==== Error Handling - -// [TODO-AI-INSTALL] Common errors the AI might encounter and their solutions. -// Delete this comment block and replace: - -[cols="1,2"] -|=== -|Error |Solution - -|[TODO-AI-INSTALL: Common error] -|[TODO-AI-INSTALL: How to fix it] -|=== - -== Example Conversation - -// [TODO-AI-INSTALL] Write a realistic example conversation showing the -// full flow from "set up X" to "done". This helps AI assistants understand -// the expected interaction pattern. -// Delete this comment block and replace: - ----- -User: Set up proven-servers from https://github.com/hyperpolymath/proven-servers - -AI: [TODO-AI-INSTALL: Write the full example conversation] ----- +== Privacy and credentials -== License +A local source review requires access to the checked-out files and whichever +local compilers or test tools the user chooses to run. No credential, API key, +secret, or external service account is required by this orientation guide. +Do not request or store secrets in chat. Do not make network calls, install +packages, alter system security settings, or interact with cloud resources +without explicit user authorization. -Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +== Current limitations -SPDX-License-Identifier: CC-BY-SA-4.0 +No supported service, root container image, published package, production +release, or deployment flow is configured. See `README.adoc`, `READINESS.adoc`, +`PROOF-NEEDS.adoc`, and `.machine_readable/6a2/STATE.a2ml` for the current +evidence boundary. diff --git a/docs/QUICKSTART.adoc b/docs/QUICKSTART.adoc index b5591d86..eacfd957 100644 --- a/docs/QUICKSTART.adoc +++ b/docs/QUICKSTART.adoc @@ -1,70 +1,74 @@ -== Quickstart - -Get up and running in 60 seconds. - -=== Prerequisites - -* https://git-scm.com/[Git] 2.40+ -* https://github.com/casey/just[just] (command runner) -* Your language toolchain (see `+Justfile+` for details) - -=== From Template (New Project) - -[source,bash] ----- -git clone https://github.com/hyperpolymath/rsr-template-repo my-project -cd my-project -rm -rf .git && git init -b main -just init # interactive placeholder replacement ----- - -=== Clone and Setup (Existing Project) +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += proven-servers — Quick Start +:toc: +:sectnums: + +[IMPORTANT] +==== +`proven-servers` is a research/prototype source monorepo of protocol models, +FFI experiments, and language-binding scaffolds. It is not a production server +distribution. Directory counts, source scans, and package builds do not prove +protocol conformance, full ABI equivalence, or deployment readiness. +==== + +== Prerequisites + +* Git 2.40 or newer. +* https://github.com/casey/just[Just] for the repository task entry point. +* Idris2 and Zig for the compiler-backed package checks. Individual package + manifests and `build.zig` files may require different toolchain versions. + Check the package documentation and manifests. The root `mise.toml` pins + Just only; Idris2 and Zig versions are not yet reproducibly pinned. + +== Clone and inspect [source,bash] ---- git clone https://github.com/hyperpolymath/proven-servers.git cd proven-servers +just --list +just info just deps ---- -=== Build and Test +`just deps` is an availability check; it does not install compilers. The +current assessment workspace lacked Just, Idris2, and Zig, so no compiler-backed +result is claimed here. + +== Build and test [source,bash] ---- -just build -just test +just build-idris # Build all discovered maintained Idris2 package manifests +just build-zig # Build all discovered Zig manifests and the root FFI example +just test-zig # Run each discovered Zig test target +just test-static # Source-pattern and binding-policy smoke checks only +just test # Aggregate compiler, Zig test, and static checks +just e2e # Bounded selected-package sweep; not full E2E/conformance ---- -=== Verify Everything Works +The aggregate targets fail when required tools are missing. A successful +package build/test establishes only the behavior exercised by that package and +test suite. Read `README.adoc`, `READINESS.adoc`, `PROOF-NEEDS.adoc`, and the +relevant package README before interpreting results. + +== Project-specific validation [source,bash] ---- -just check +just validate-rsr # Local metadata paths and synchronized Justfile snapshot +just fmt-check # Git whitespace checks; not a source formatter +just lint # Shell syntax and binding-policy checks ---- -=== Project Structure - -.... -src/ # Source code -tests/ # Test suite -benches/ # Benchmarks -docs/ # Documentation -.github/ # CI/CD workflows -.... - -=== What Next? - -* Browse the link:.[docs/] for architecture and conventions -* Run `+just --list+` to see all available commands -* Read link:../.github/CONTRIBUTING.md[CONTRIBUTING.md] when you are ready to -contribute - -=== Troubleshooting +`just validate-rsr` is a bounded repository-shape check, not an RSR +certification. Static smoke checks are not formal proof, executable behavior, +security assurance, ABI-wide equivalence, or protocol conformance. -If `+just deps+` fails, ensure your toolchain version matches the -project requirements listed in the `+Justfile+` or -`+.machine_readable/ECOSYSTEM.a2ml+`. +== Contribution guidance -Open a -https://github.com/hyperpolymath/proven-servers/discussions[Discussion] -if you get stuck. +Read `.github/CONTRIBUTING.md` and `QUICKSTART-DEV.adoc` before editing. Do not +run template bootstrap or global placeholder replacement: this repository has +no `just init` task. Container, cloud deployment, release, and Pages publishing +workflows are not configured as validated product deliverables. diff --git a/docs/README.adoc b/docs/README.adoc index d25a1922..2d20b603 100644 --- a/docs/README.adoc +++ b/docs/README.adoc @@ -2,15 +2,23 @@ // Copyright (c) Jonathan D.A. Jewell = Documentation Layout -Primary tracks: +This directory contains project-specific guidance, design records, and +historical maintenance material. Empty topic folders are organizational slots, +not evidence that formal theory or whitepapers have been authored. -* `theory/` for formal and conceptual material -* `practice/` for operational and implementation material -* `maintenance/` for baseline checklists and release hard-pass runbooks -* `whitepapers/academic/` for research-facing whitepapers -* `whitepapers/industry/` for industry/outreach whitepapers +== Current paths -Core docs: +* `QUICKSTART.adoc` — repository-scoped quick start. +* `THREAT-MODEL.adoc` — scoped threat inventory; not a security audit. +* `TOPOLOGY-GUIDE.adoc` — how to update the source-tree map. +* `decisions/` — selected architecture decision records (`.adoc`). +* `maintenance/MAINTENANCE-CHECKLIST.adoc` — project-specific maintenance + checklist. +* `practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc` — development process. +* `design/` — dated technical design notes; verify status before relying on + them. +* `theory/` and `whitepapers/` — currently empty topic directories. -* `maintenance/MAINTENANCE-CHECKLIST.md` -* `practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc` +The canonical top-level README, readiness, security, proof-needs, and +maintainer docs remain at the repository root. No docs-site publication or +repository-wide documentation build is currently configured. diff --git a/docs/THREAT-MODEL.adoc b/docs/THREAT-MODEL.adoc index d9e13009..45d35c65 100644 --- a/docs/THREAT-MODEL.adoc +++ b/docs/THREAT-MODEL.adoc @@ -1,254 +1,133 @@ -== Threat Model: proven-servers +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += Threat Model — proven-servers Source Monorepo +:toc: +:sectnums: +:revdate: 2026-09-27 -=== Document Info - -[cols=",",options="header",] -|=== -|Field |Value -|Project |proven-servers -|Version |1.0 -|Last Reviewed |2026-03-02 -|Author |Jonathan D.A. Jewell -|Methodology |STRIDE -|=== - -=== Scope - -==== In Scope - -* Application source code and build pipeline -* CI/CD workflows (GitHub Actions) -* Container images and runtime environment -* Secrets and credential management -* Dependencies (direct and transitive) -* Deployment artifacts (binaries, containers, SBOM) - -==== Out of Scope - -* Physical security of hosting infrastructure -* GitHub/GitLab platform-level vulnerabilities -* End-user device security -* Social engineering attacks against maintainers (handled by org policy) - -=== System Overview - -Brief description of proven-servers and its architecture. - -____ -See link:../TOPOLOGY.md[TOPOLOGY.md] for the full architecture diagram -and completion dashboard. -____ - -=== Assets - -[width="100%",cols="25%,16%,13%,46%",options="header",] -|=== -|Asset |Classification |Owner |Notes -|Source code |Internal |Maintainers |Public repos are still -internal-integrity - -|Signing keys |Restricted |Release lead |Signing keys (e.g., Ed25519), -GPG keys - -|CI/CD secrets |Restricted |Maintainers |GITHUB_TOKEN, deploy tokens, -PATs - -|User/contributor data |Confidential |Org |Emails, contributor identity - -|Build artifacts |Internal |CI pipeline |Binaries, WASM bundles - -|Container images |Internal |CI pipeline |Chainguard-based, signed via -image signing tool - -|SBOM / provenance |Public |CI pipeline |SLSA attestations - -|Dependencies |Public |Lockfile |Cargo.lock, deno.lock, gleam.toml - -|Infrastructure config |Confidential |Maintainers |Containerfiles, -compose files, orchestration config -|=== - -=== Trust Boundaries - -[width="100%",cols="35%,32%,33%",options="header",] -|=== -|Boundary |From (Lower Trust) |To (Higher Trust) -|Pull request submission |External contributor |Repository codebase - -|CI/CD workflow execution |Workflow definition |Runner with secrets -access - -|Container build boundary |Build stage |Runtime stage - -|External API calls |Third-party service |Application internals - -|User input (CLI/Web) |End user |Application logic - -|Dependency resolution |Package registry |Build environment - -|Forge mirroring |GitHub |GitLab / Bitbucket -|=== - -=== Threat Actors - -[width="100%",cols="39%,44%,17%",options="header",] -|=== -|Actor |Motivation |Capability -|Script kiddie |Vandalism, clout |Low -|Disgruntled contributor |Sabotage, backdoor insertion |Medium -|Supply chain attacker |Wide-impact compromise |High -|Nation state |Espionage, disruption |Very High -|Automated bot |Credential stuffing, spam PRs |Low-Medium -|=== - -=== STRIDE Analysis - -==== Spoofing - -[width="100%",cols="28%,14%,9%,6%,6%,37%",options="header",] -|=== -|Threat |Affected Asset |Likelihood |Impact |Risk |Mitigation -|Unsigned commits impersonate maintainer |Source code |Medium |High -|High |Require GPG-signed commits; vigilant code review - -|Forged bot actions (automated agents) |CI/CD pipeline |Low |High -|Medium |Bot tokens scoped minimally; audit bot activity - -|Spoofed package registry identity |Dependencies |Low |High |Medium |Pin -dependencies by hash; verify provenance -|=== - -==== Tampering - -[width="100%",cols="28%,14%,9%,6%,6%,37%",options="header",] -|=== -|Threat |Affected Asset |Likelihood |Impact |Risk |Mitigation -|Malicious pull request |Source code |Medium |High |High |Branch -protection; required reviews; CodeQL - -|Dependency poisoning (typosquat) |Dependencies |Medium |High |High -|Lockfiles; secret-scanner; security scans - -|Tampered container base image |Container images |Low |High |Medium -|Chainguard images; image signing verification - -|Workflow file modification |CI/CD pipeline |Low |High |Medium -|CODEOWNERS on .github/; workflow-linter -|=== - -==== Repudiation - -[width="100%",cols="28%,14%,9%,6%,6%,37%",options="header",] -|=== -|Threat |Affected Asset |Likelihood |Impact |Risk |Mitigation -|Unlogged deployment |Build artifacts |Medium |Medium |Medium |SLSA -provenance; deployment audit trail - -|Denied merge of vulnerable code |Source code |Low |Medium |Low |Git -history is immutable; signed commits - -|Secret rotation without record |CI/CD secrets |Low |Low |Low |Secret -rotation logged in STATE.a2ml -|=== - -==== Information Disclosure - -[width="100%",cols="28%,14%,9%,6%,6%,37%",options="header",] -|=== -|Threat |Affected Asset |Likelihood |Impact |Risk |Mitigation -|Secrets leaked in git history |CI/CD secrets |Medium |High |High -|TruffleHog in CI; secret-scanner workflow - -|Verbose error messages in prod |Application logic |Medium |Medium -|Medium |Sanitize outputs; structured logging - -|SBOM reveals internal structure |Infrastructure |Low |Low |Low -|Accepted risk; SBOM is intentionally public -|=== - -==== Denial of Service - -[width="100%",cols="28%,14%,9%,6%,6%,37%",options="header",] -|=== -|Threat |Affected Asset |Likelihood |Impact |Risk |Mitigation -|CI resource exhaustion (fork bomb in PR) |CI/CD pipeline |Medium -|Medium |Medium |Concurrency limits; timeout on workflows - -|Spam issues/PRs flooding triage |Maintainer time |Medium |Low |Low -|GitHub rate limits; bot auto-close stale - -|Large binary commits bloating repo |Source code |Low |Medium |Low -|.gitattributes LFS policy; pre-commit hooks -|=== - -==== Elevation of Privilege - -[width="100%",cols="28%,14%,9%,6%,6%,37%",options="header",] -|=== -|Threat |Affected Asset |Likelihood |Impact |Risk |Mitigation -|Workflow injection via PR title/body |CI/CD pipeline |Medium |High -|High |Never interpolate PR fields in `+run:+`; use env vars - -|GITHUB_TOKEN over-scoped |CI/CD secrets |Medium |High |High -|`+permissions: read-all+` default; per-job scoping - -|Container escape |Runtime environment |Low |High |Medium |Hardened -container runtime; read-only rootfs; no-new-privileges - -|Compromised action dependency |CI/CD pipeline |Medium |High |High -|SHA-pin all actions; never use `+@latest+` tags -|=== - -=== Mitigations in Place - -* *SLSA Provenance*: Build attestations via slsa-github-generator -* *Secret Scanning*: TruffleHog + secret-scanner workflow on every push -* *Static Analysis*: CodeQL on supported languages -* *Supply Chain*: OpenSSF Scorecard (scorecard.yml + -scorecard-enforcer.yml) -* *Container Signing*: Ed25519 signatures on all published images -(optional: use your signing tool) -* *Container Runtime*: Hardened container runtime with formal -verification (optional) -* *Dependency Pinning*: All GitHub Actions SHA-pinned; lockfiles -committed -* *Workflow Validation*: workflow-linter.yml checks all workflow changes -* *Security Scanning*: Neurosymbolic scanning (hypatia-scan.yml, -optional) -* *Bot Governance*: Bot orchestration with confidence thresholds -(optional) -* *Edge Security*: Gateway with policy enforcement (optional, where -applicable) -* *SBOM*: Generated and published with releases - -=== Residual Risks - -[width="100%",cols="39%,41%,20%",options="header",] -|=== -|Risk |Accepted Because |Review Trigger -|Zero-day in GitHub Actions runner |Platform responsibility; no feasible -mitigation |GitHub advisory - -|Maintainer account compromise |Mitigated by 2FA requirement; residual -remains |Any suspicious activity - -|Transitive dependency vulnerability (0-day) |Lockfiles limit blast -radius; scanning catches known CVEs |CVE database update - -|SBOM exposes internal component names |Transparency is a design goal -|Policy change -|=== - -=== Review Schedule - -This threat model should be reviewed: - -* *Quarterly* as a standing item -* *When architecture changes* (new services, new trust boundaries, new -deployment targets) -* *Before major releases* (v1.0, v2.0, etc.) -* *After any security incident* affecting this project or its -dependencies - -Reviewer should update the "`Last Reviewed`" date and version in -Document Info above. +[IMPORTANT] +==== +This document is a scoped threat inventory, not a security audit or assurance +claim. It was reconciled against repository configuration on 2026-09-27; no +runtime, dependency, secret, or CI check was executed as part of that review. +==== + +== Scope + +This model covers the public source monorepo, package/build/test configuration, +maintainer workstations, and the GitHub/GitLab automation declared in the +checkout. The project is a research/prototype collection of protocol models, +Idris2 and Zig source, FFI experiments, and binding scaffolds. + +There is no validated production network service, root container image, +release artifact, signing pipeline, or live deployment topology in scope. The +experimental connector code and historical deployment configuration must not +be treated as an authorized service. + +Out of scope are the security of GitHub/GitLab infrastructure, maintainer +personal devices, external prover/database services, and any hypothetical +deployment not represented by a reviewed, reproducible project configuration. + +== Assets and trust boundaries + +[cols="1,2,3",options="header"] +|=== +|Asset |Trust boundary |Current evidence/limitation + +|Source and model code +|Contributor changes enter through review and CI. +|Public source; compiler/runtime status is package- and revision-specific. + +|Maintainer identities and repository settings +|Account authentication, branch rules, GitHub/GitLab administration. +|Not independently assessed by this repository review. + +|CI workflow definitions and third-party actions +|Workflow code and fetched actions execute on hosted runners. +|Some actions use version tags rather than commit SHAs. The central estate +suite is pinned to the commit recorded in `.github/workflows/actions.lock`; +that lock remains inventory metadata and does not by itself enforce integrity. + +|Package/compiler dependencies +|Build manifests resolve tools and packages from external sources. +|No complete lockfile/pinning or current vulnerability scan has been verified. + +|Credentials +|CI secrets and local environment variables are available only to the jobs or +processes that receive them. +|No release/deploy credential use is intended in the validated task set; audit +workflow permissions and repository settings before enabling new secrets. + +|Build outputs +|Local build directories and generated headers are source/build boundaries. +|Generated ABI/header correspondence is not established repository-wide. +|=== + +== Principal threats + +[cols="1,3,3",options="header"] +|=== +|Threat |Potential impact |Review/mitigation action + +|Malicious pull request or workflow edit +|Execution of untrusted code on CI or compromise of repository contents. +|Keep permissions minimal; review workflow changes; do not expose secrets to +untrusted code; require appropriate protected-branch review. + +|Mutable third-party action reference +|Upstream ref changes can alter code executed in a privileged workflow. +|Prefer full commit-SHA pins; verify action provenance; treat the action lock +as informational unless a check enforces correspondence. + +|Dependency or toolchain substitution +|A compromised package/compiler download can corrupt builds or local work. +|Use package-specific immutable versions and lockfiles where supported; review +install scripts before running; avoid `curl | sh` patterns. + +|False verification/readiness claims +|Users may deploy untested or incomplete cryptographic/network code. +|Distinguish source inventory, compiler checks, executable tests, formal +proofs, conformance, and deployment evidence. Keep unavailable operations +fail-closed and document the exact tested revision. + +|Credential leakage in code, logs, or generated artifacts +|Unauthorized access to external services or maintainer accounts. +|Do not commit secrets; avoid logging tokens; scan before publishing; scope +secrets narrowly and rotate them through the forge's approved process. + +|Accidental external deployment or release +|Cloud charges, data exposure, or publication of an unvalidated artifact. +|Root container/cloud deployment, package release, and Pages publishing are +not configured as product operations. Connector-specific experimental Fly.io +deployment is explicitly disabled. Do not restore deployment automation without +review and explicit authorization. +|=== + +== Existing controls and their limits + +* `.github/workflows/` contains CI, static-analysis, and security-related + workflow definitions. Their presence is not evidence that they ran or passed + on this revision. +* `.github/workflows/actions.lock` records action references and resolved + metadata, but is not a runtime SHA pin unless a workflow enforces it. +* `Justfile` has compiler-backed build/test tasks and explicitly scoped static + smoke checks. Missing toolchains cause required checks to fail rather than + print a success placeholder. +* `setup.sh` is a non-mutating information helper; it does not install + packages, change firewall/SELinux state, or execute downloaded scripts. +* Security contact and disclosure instructions live in `SECURITY.adoc` and + `.well-known/security.txt`. No verified OpenPGP key is currently published. + +These controls have not been evaluated here for completeness, effectiveness, +or passing status. No claim is made of SLSA provenance, signed container +images, GPG-signed commits, a specific OpenSSF score, or a security +certification. + +== Residual risks and next review + +The main residual risks are unverified package/runtime behavior; incomplete +binding and ABI conformance; mutable or insufficiently reviewed CI dependencies; +and documentation drift. Revisit this model when workflow permissions, +third-party action references, dependency resolution, credential use, or a +real service/release/deployment target changes. Record actual scan and test +results separately from this threat inventory. diff --git a/docs/TOPOLOGY-GUIDE.adoc b/docs/TOPOLOGY-GUIDE.adoc index 62529112..314214c1 100644 --- a/docs/TOPOLOGY-GUIDE.adoc +++ b/docs/TOPOLOGY-GUIDE.adoc @@ -1,156 +1,33 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -= TOPOLOGY.md — Generation Guide -Jonathan D.A. Jewell (hyperpolymath) += Repository Topology — Update Guide :toc: :sectnums: +:revdate: 2026-09-27 -== What Is TOPOLOGY.md? +The human-readable source-tree map is `../TOPOLOGY.adoc`. It describes the +current repository inventory and evidence limits; it is not a generated +architecture diagram, completion dashboard, or readiness grade. -A single-file visual map of any project's architecture and completion status. -It lives in the repo root and contains: +== Update procedure -1. **ASCII architecture diagram** — the full system as it will look when complete -2. **Completion dashboard** — every component with a progress bar and status note -3. **Dependency graph** — what blocks what (the critical path) -4. **Update protocol** — how to keep it current +When the top-level source layout changes: -It is designed to be readable by humans, AI agents, and rendered cleanly on any -forge (GitHub, GitLab, Codeberg, Bitbucket). +. Inspect the actual filesystem and Git-tracked paths. +. Update `TOPOLOGY.adoc` and the matching counts/qualifications in + `README.adoc` only when the directory inventory has changed. +. Keep directory counts separate from build, test, proof, support, or release + claims. +. Update `READINESS.adoc`, `PROOF-NEEDS.adoc`, and + `.machine_readable/6a2/STATE.a2ml` only when new evidence has been reproduced. +. Check paths and whitespace with `just validate-rsr` and `just fmt-check`. -== Why +Do not use estimated percentages, progress bars, or source-directory presence +to imply completion. There is no automatic topology generator configured. -- Gives any contributor (human or AI) an instant picture of the whole project -- Replaces "read 20 files to understand the architecture" with one glance -- The completion dashboard makes project health visible without running anything -- Works offline, no tooling required, just a text file +== Related sources -== How To Generate One - -=== Option 1: Ask an AI agent - -Use this prompt (works with Claude, Gemini, ChatGPT, or any LLM with repo access): - -[source,text] ----- -Read the entire repository and produce a TOPOLOGY.md file for the repo root. - -The file must contain exactly three sections: - -1. **System Architecture** — An ASCII box diagram showing the complete system - as it will look when finished. Use Unicode box-drawing characters - (┌ ┐ └ ┘ │ ─ ├ ┤ ┬ ┴ ┼), arrows (▲ ▼ ◄ ► → ←), and double lines - (═ ║) for boundaries. Show: - - All external services (DNS, CDN, gateways) at the top - - Application components in the middle - - Data layer (databases, caches, queues) below - - Repo infrastructure (CI, contractiles, SCM files) at the bottom - - Every box labelled, every connection labelled or obvious from context - - The diagram should be BESPOKE to this project, not generic - -2. **Completion Dashboard** — A table in a code block listing every component - from the diagram. For each component show: - - Name (left-aligned, padded to 35 chars) - - Progress bar: 10 characters using █ (done) and ░ (remaining) - - Percentage (0% to 100% in 10% increments) - - A short note explaining the status - Group components by layer/concern. End with an OVERALL summary line. - -3. **Key Dependencies** — An ASCII arrow diagram showing the critical path. - What must finish before what else can start. - -Add a header comment with SPDX-License-Identifier and Last updated date. -End with an "Update Protocol" section explaining how to maintain the file. - -Use the template at TOPOLOGY.md in rsr-template-repo as a structural reference, -but make the content completely specific to THIS project. ----- - -=== Option 2: Copy the template and fill it in - -[source,bash] ----- -cp /path/to/rsr-template-repo/TOPOLOGY.md ./TOPOLOGY.md -# Then edit: replace placeholders, draw the real architecture, fill the dashboard ----- - -=== Option 3: Batch generation across all repos - -[source,bash] ----- -# From the repos root, generate for every repo that lacks one -for repo in /path/to/your/repos/*/; do - if [ ! -f "$repo/TOPOLOGY.md" ]; then - echo "NEEDS TOPOLOGY: $(basename $repo)" - fi -done ----- - -Then feed each repo to an AI agent with the prompt above. Claude Code can do -this with a session per repo, or you can batch it. - -== Conventions - -=== Box-drawing characters - -Use Unicode, not ASCII art. This renders correctly everywhere. - -[cols="1,1", options="header"] -|=== -| Character | Use -| `┌ ┐ └ ┘` | Box corners -| `│ ─` | Vertical / horizontal lines -| `├ ┤ ┬ ┴ ┼` | T-junctions and crosses -| `═ ║` | Double lines for major boundaries -| `▲ ▼ ◄ ►` | Directional arrows -| `→ ← ↑ ↓` | Thin arrows (alternative) -|=== - -=== Progress bars - -Always 10 characters wide. Use full blocks only (no half-blocks). - -[source,text] ----- -░░░░░░░░░░ 0% Not started -█░░░░░░░░░ 10% Stub/skeleton exists -██░░░░░░░░ 20% Early work -███░░░░░░░ 30% Foundation laid -████░░░░░░ 40% Core logic started -█████░░░░░ 50% Half done -██████░░░░ 60% Most logic complete -███████░░░ 70% Working but rough -████████░░ 80% Needs polish/docs -█████████░ 90% Nearly done -██████████ 100% Complete and tested ----- - -=== Component naming - -- Use the actual names from the codebase (file names, service names, tool names) -- Group by architectural layer, not alphabetically -- Include repo infrastructure (CI, contractiles, SCM files) as a layer - -=== When to update - -- After completing a component → change bar + percentage -- After adding a component → add row -- After architectural change → redraw diagram -- After major milestone → update overall percentage -- Always update the `Last updated` date - -== Integration With Other RSR Files - -TOPOLOGY.md complements but does not replace: - -- **STATE.a2ml** — machine-readable state (tasks, blockers, next actions) -- **ECOSYSTEM.a2ml** — position in the wider project ecosystem -- **META.a2ml** — architecture decisions and design rationale -- **0-AI-MANIFEST.a2ml** — AI agent entry point and invariants - -TOPOLOGY.md is the _visual summary_ for humans; the a2ml files are the -_structured data_ for tooling. Both should agree. - -== Copyright - -Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +* `../README.adoc` — project scope and inventory. +* `../READINESS.adoc` — current verification status. +* `../PROOF-NEEDS.adoc` — outstanding proof and conformance evidence. +* `../.machine_readable/6a2/STATE.a2ml` — machine-readable current state. diff --git a/docs/decisions/0000-template.adoc b/docs/decisions/0000-template.adoc index de603adf..fdda748d 100644 --- a/docs/decisions/0000-template.adoc +++ b/docs/decisions/0000-template.adoc @@ -5,7 +5,7 @@ Date: YYYY-MM-DD === Status {empty}[Proposed | Accepted | Deprecated | Superseded by -link:NNNN-title.md[ADR-NNNN] | Rejected] +link:NNNN-title.adoc[ADR-NNNN] | Rejected] === Context diff --git a/docs/decisions/0001-adopt-rsr-standard.adoc b/docs/decisions/0001-adopt-rsr-standard.adoc index 0dbd05a3..89291858 100644 --- a/docs/decisions/0001-adopt-rsr-standard.adoc +++ b/docs/decisions/0001-adopt-rsr-standard.adoc @@ -6,14 +6,23 @@ Date: 2026-02-14 Accepted +=== Current applicability + +This is a historical decision record, not a current compliance inventory. The +current repository shape and evidence limits are documented in +`0-AI-MANIFEST.a2ml`, `README.adoc`, `.machine_readable/rsr-profile.a2ml`, and +`READINESS.adoc`. Template features listed below are not assumed to exist or be +enabled in this repository; action pinning and capability applicability must +be checked against the current checkout. + === Context Managing multiple repositories with an ad-hoc approach led to significant inconsistencies across the ecosystem. Common problems included: -* Missing or incomplete configuration files (SECURITY.md, -CONTRIBUTING.md, .editorconfig, etc.) +* Missing or incomplete configuration files (security policy, contributing +guidance, editor configuration, etc.) * State files (STATE.a2ml, META.a2ml, ECOSYSTEM.a2ml) placed in the repository root instead of the canonical `+.machine_readable/+` directory @@ -45,11 +54,11 @@ all AI agents orchestration integration * *Contractiles* in `+.machine_readable/contractiles/+` (k9, dust, lust, must, trust) for policy enforcement -* *Standardized workflows* (16+ GitHub Actions workflows, all -SHA-pinned) +* *Standardized workflow patterns* with action-version metadata; action + references and pinning must be verified for each consuming repository * *Justfile automation* with standard recipes for common tasks -* *Security and governance files*: SECURITY.md, CONTRIBUTING.md, -CODE_OF_CONDUCT.md, LICENSE (MPL-2.0) +* *Security and governance guidance* in the formats and paths selected by each + repository, alongside a LICENSE * *Architecture Decision Records* in `+docs/decisions/+` New repositories are created by cloning the template: diff --git a/docs/decisions/README.adoc b/docs/decisions/README.adoc index 3dc7a485..729ced48 100644 --- a/docs/decisions/README.adoc +++ b/docs/decisions/README.adoc @@ -1,18 +1,17 @@ -== Architecture Decision Records +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += Architecture Decision Records -We record significant architectural decisions using -https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions[Architecture -Decision Records (ADRs)], as described by Michael Nygard. +Significant architectural choices are recorded as dated ADR documents in this +directory. The current records are selected history, not a complete or +machine-validated decision log. -Each ADR captures the context, decision, and consequences of a choice -that affects the project’s structure, dependencies, or conventions. +== Create an ADR -=== Creating a new ADR +There is no `just adr` generator. Copy `0000-template.adoc` to the next +available numbered `.adoc` filename, replace every example section, and review +its paths, status, and evidence before opening a pull request. -[source,bash] ----- -just adr "Title of decision" ----- - -This creates a new numbered file in `+docs/decisions/+` from the -template at `+0000-template.md+`. +Use `Proposed` until the maintainer accepts the decision. If a later decision +changes an earlier one, link the records and update the earlier status without +rewriting its historical context. diff --git a/docs/design/DESIGN-2026-03-01-connector-abi-ffi.adoc b/docs/design/DESIGN-2026-03-01-connector-abi-ffi.adoc index 7b67fce4..44da23a5 100644 --- a/docs/design/DESIGN-2026-03-01-connector-abi-ffi.adoc +++ b/docs/design/DESIGN-2026-03-01-connector-abi-ffi.adoc @@ -1,373 +1,52 @@ -== Connector ABI-FFI Design — 2026-03-01 - -=== Summary - -This document describes the formal ABI and FFI layers for all 6 -*proven-servers connector interfaces*: `+dbconn+`, `+authconn+`, -`+cacheconn+`, `+queueconn+`, `+resolverconn+`, and `+storageconn+`. - -Each connector follows a uniform four-layer architecture: - -[arabic] -. *Idris2 ABI* — Dependent-type definitions proving tag encodings, -state-machine transitions, and capability witnesses -. *C Header* — Auto-generated tag constants, opaque struct typedefs, and -function declarations -. *Zig FFI* — Runtime state-machine enforcement via exported -`+callconv(.c)+` functions -. *Zig Tests* — ABI version checks, lifecycle tests, invalid-transition -rejection, NULL safety, and enum tag consistency - -=== Motivation - -The proven-servers connectors define the _interfaces_ between a formally -verified server core and external infrastructure (databases, caches, -queues, storage, DNS, authentication). These interfaces must be: - -* *Correct by construction* — invalid state transitions are impossible -at the type level (Idris2 `+impossible+` keyword eliminates bad cases) -* *Language-agnostic* — any language that can call C can use the -connector -* *Testable* — runtime behaviour matches the compile-time proofs - -The ABI-FFI pattern achieves all three. The Idris2 ABI _proves_ the -state machine is sound; the Zig FFI _enforces_ it at runtime; the C -header lets _any_ language consume it. - -=== Architecture - -.... - ┌─────────────────────────────────────────────────────┐ - │ Idris2 ABI (compile-time proofs) │ - │ │ - │ Layout.idr Tag encodings + roundtrip proofs │ - │ Transitions.idr GADT state machine + witnesses │ - │ Foreign.idr Opaque handles + FFI contract │ - └───────────────────────┬─────────────────────────────┘ - │ generates - ▼ - ┌─────────────────────────────────────────────────────┐ - │ C Header (generated/abi/.h) │ - │ Tag #defines · opaque structs · function decls │ - └───────────────────────┬─────────────────────────────┘ - │ imported by - ▼ - ┌─────────────────────────────────────────────────────┐ - │ Zig FFI (ffi/zig/src/.zig) │ - │ enum(u8) types · handle structs · exported fns │ - └───────────────────────┬─────────────────────────────┘ - │ tested by - ▼ - ┌─────────────────────────────────────────────────────┐ - │ Zig Tests (ffi/zig/test/_test.zig) │ - │ ABI version · lifecycle · NULL safety · tag match │ - └─────────────────────────────────────────────────────┘ -.... - -=== Per-Connector State Machines - -==== proven-dbconn (reference implementation) - -.... -States: Disconnected(0) Connected(1) InTransaction(2) Prepared(3) Failed(4) -Transitions: - Connect: Disconnected → Connected - Disconnect: Connected → Disconnected - BeginTx: Connected → InTransaction - Commit: InTransaction → Connected - Rollback: InTransaction → Connected - Prepare: Connected → Prepared - Execute: Prepared → Connected - ConnFail: Connected → Failed - Reset: Failed → Disconnected -Capabilities: - CanQuery: Connected only - CanBeginTx: Connected only -.... - -==== proven-authconn - -.... -States: Unauthenticated(0) Challenging(1) Authenticated(2) - Expired(3) Revoked(4) Locked(5) -Transitions: - InitAuth: Unauthenticated → Challenging (MFA methods) - DirectAuth: Unauthenticated → Authenticated (password, apikey, cert, opaque) - LockOut: Unauthenticated → Locked (max failed attempts) - ChallengeOk: Challenging → Authenticated - ChallengeFail: Challenging → Unauthenticated - ChallengeLock: Challenging → Locked - SessionExpire: Authenticated → Expired - Revoke: Authenticated → Revoked - ReAuth: Expired → Authenticated - ResetRevoked: Revoked → Unauthenticated - Unlock: Locked → Unauthenticated -Capabilities: - CanAuthenticate: Unauthenticated only - CanAccessResource: Authenticated only -Constants: - MAX_TOKEN_LIFETIME = 3600s - MAX_REFRESH_LIFETIME = 86400s - MAX_LOGIN_ATTEMPTS = 5 - LOCKOUT_DURATION = 900s -.... - -==== proven-cacheconn - -.... -States: Disconnected(0) Connected(1) Degraded(2) Failed(3) -Transitions: - Connect: Disconnected → Connected - ConnectFail: Disconnected → Failed - Disconnect: Connected → Disconnected - Degrade: Connected → Degraded - ConnDrop: Connected → Failed - Recover: Degraded → Connected - FullFailure: Degraded → Failed - Reset: Failed → Disconnected -Capabilities: - CanOperate: Connected or Degraded - CanFlush: Connected only (not degraded) -Constants: - DEFAULT_TTL = 3600s - MAX_KEY_LENGTH = 512 - MAX_VALUE_SIZE = 1048576 (1 MiB) -.... - -==== proven-queueconn - -.... -States: Disconnected(0) Connected(1) Consuming(2) Producing(3) Failed(4) -Transitions: - Connect: Disconnected → Connected - ConnectFail: Disconnected → Failed - Subscribe: Connected → Consuming - Unsubscribe: Consuming → Connected - Publish: Connected → Producing (brief) - PublishDone: Producing → Connected - ConsumeFail: Consuming → Failed - ProduceFail: Producing → Failed - Disconnect: Connected → Disconnected - ConsDrop: Consuming → Disconnected - Reset: Failed → Disconnected -Capabilities: - CanConsume: Consuming only - CanProduce: Producing only (Connected can initiate) - CanSubscribe: Connected only -Constants: - MAX_MESSAGE_SIZE = 1048576 (1 MiB) - DEFAULT_PREFETCH = 10 - ACK_TIMEOUT = 30s -.... - -==== proven-resolverconn - -.... -States: Ready(0) Querying(1) Cached(2) Failed(3) -Transitions: - Query: Ready → Querying - CacheHit: Ready → Cached - InitFail: Ready → Failed - QueryComplete: Querying → Ready - StoreResult: Querying → Cached - QueryFail: Querying → Failed - CacheExpire: Cached → Ready - RefreshQuery: Cached → Querying - Reset: Failed → Ready -Capabilities: - CanResolve: Ready only - CanServe: Ready or Cached -Record Types (13): - A(0) AAAA(1) CNAME(2) MX(3) NS(4) SOA(5) TXT(6) SRV(7) - PTR(8) CAA(9) TLSA(10) HTTPS(11) SVCB(12) -Constants: - DEFAULT_TIMEOUT = 5s - MAX_RETRIES = 3 - MAX_CACHE_ENTRIES = 10000 - MIN_TTL = 60s -.... - -==== proven-storageconn - -.... -States: Disconnected(0) Connected(1) Uploading(2) Downloading(3) Failed(4) -Transitions: - Connect: Disconnected → Connected - ConnectFail: Disconnected → Failed - StartUpload: Connected → Uploading - StartDownload: Connected → Downloading - UploadDone: Uploading → Connected - UploadFail: Uploading → Failed - DownloadDone: Downloading → Connected - DownloadFail: Downloading → Failed - Disconnect: Connected → Disconnected - UploadCancel: Uploading → Connected - Reset: Failed → Disconnected -Capabilities: - CanOperate: Connected only (Uploading/Downloading are busy) -Constants: - MAX_OBJECT_SIZE = 5368709120 (5 GiB) - MAX_KEY_LENGTH = 1024 - MAX_BUCKET_NAME_LEN = 63 -.... - -=== Idris2 ABI Layer Detail - -Each connector’s Idris2 ABI consists of three modules: - -==== Layout.idr - -Defines tag encodings as `+Bits8+` with three components per type: - -[arabic] -. *Size constant* — `+Size : Nat+` (number of variants) -. *Encoder* — `+ToTag : -> Bits8+` (type → tag) -. *Decoder* — `+tagTo : Bits8 -> Maybe +` (tag → type) -. *Roundtrip proof* — -`+tagToRoundtrip : (x : ) -> tagTo (ToTag x) = Just x+` - -The roundtrip proof ensures the encoder and decoder are consistent — -encoding then decoding always recovers the original value. This is -proved by case-splitting on every variant, each reducing to `+Refl+`. - -==== Transitions.idr - -Defines a GADT `+ValidTransition : -> -> Type+` where -each constructor names a legal transition: - -[source,idris] ----- -data ValidTransition : AuthState -> AuthState -> Type where - InitAuth : ValidTransition Unauthenticated Challenging - DirectAuth : ValidTransition Unauthenticated Authenticated - ... ----- - -*Capability witnesses* are predicates on single states: - -[source,idris] ----- -data CanAuthenticate : AuthState -> Type where - AuthWhenUnauth : CanAuthenticate Unauthenticated ----- - -*Impossibility proofs* use the `+impossible+` keyword to eliminate -nonsensical states: - -[source,idris] ----- -noAuthFromLocked : CanAuthenticate Locked -> Void -noAuthFromLocked x impossible ----- - -*Decidability procedures* return `+Dec (CanX s)+` for any state `+s+`, -allowing callers to branch on capability at compile-time: - -[source,idris] ----- -decCanAuthenticate : (s : AuthState) -> Dec (CanAuthenticate s) -decCanAuthenticate Unauthenticated = Yes AuthWhenUnauth -decCanAuthenticate _ = No (\case AuthWhenUnauth impossible) ----- - -==== Foreign.idr - -Declares opaque handle types and the FFI contract: - -[source,idris] ----- -data SessionHandle : Type where [external] ----- - -The `+[external]+` pragma tells Idris2 the type has no Idris-side -representation — it exists only as a pointer in the FFI layer. The -module also documents the ABI version and lists all exported functions -with their Zig/C signatures. - -=== Zig FFI Layer Detail - -Each Zig implementation: - -[arabic] -. Defines `+enum(u8)+` types matching the C header tag values exactly -. Defines a handle `+struct+` holding the current `+state+` and any -connection parameters (port, TLS flag, etc.) -. Uses `+std.heap.GeneralPurposeAllocator+` for handle allocation -. Exports functions with `+pub export fn ... callconv(.c)+` that: -* Return early with an error on NULL handles (`+orelse return ...+`) -* Switch on `+handle.state+` to enforce the state machine -* Return domain-appropriate errors for invalid states - -==== Naming Conventions - -* Zig enums use `+snake_case+` matching the Idris2 constructors -* Zig avoids keyword collisions: `+none_+` for `+None+` -(IntegrityCheck), `+opaque_+` for `+Opaque+` (CredentialType) -* C header constants use `+SCREAMING_SNAKE_CASE+` with a connector -prefix: `+AUTHCONN_STATE_AUTHENTICATED+`, -`+STORAGECONN_ERR_PATH_TRAVERSAL+` - -=== Error Tag Convention - -All connectors reserve *tag 0 for "`no error`"*. Error variants begin at -tag 1. This is consistent across all 6 connectors and matches the C -convention where 0 indicates success. - -=== Testing Strategy - -Each connector has a Zig test file exercising: - -[width="100%",cols="44%,56%",options="header",] -|=== -|Category |Description -|ABI version |`+_abi_version()+` returns 1 - -|Happy path |Connect → operate → disconnect lifecycle - -|Invalid transitions |Operations in wrong state return errors - -|NULL safety |All functions handle NULL handles gracefully - -|Enum tag consistency |Every variant’s `+@intFromEnum+` matches the C -header tag -|=== - -Test counts: authconn (20), cacheconn (13), queueconn (17), resolverconn -(12), storageconn (14), dbconn (per prior session). - -=== Build - -Each connector builds independently: - -[source,bash] ----- -cd connectors/proven-/ffi/zig -zig build # shared + static libraries -zig build test # run integration tests -zig build -Doptimize=ReleaseFast # optimised build ----- - -Output: `+zig-out/lib/libproven_.so+` (shared) and -`+zig-out/lib/libproven_.a+` (static). - -=== Verification Summary - -All 6 connectors verified on 2026-03-01: - -* *Zig 0.15.2* — `+zig build+` produces 0 errors for all 6 -* *Zig 0.15.2* — `+zig build test+` passes for all 6 -* *Reversal test* — checking out `+c7f6796+` (prior commit) confirms all -FFI directories are absent and builds fail; restoring `+2011a1f+` -confirms all files are present and builds succeed - -=== Future Work - -* *Language bindings* — Rust, ReScript, Gleam, Elixir wrappers consuming -the C ABI -* *Core primitives ABI-FFI* — same pattern for the 8 core primitives -(socket, frame, fsm, wire, compose, tls, config, audit) -* *Protocol ABI-FFI* — 94 protocol skeletons (lower priority; connectors -and core are the integration surface) -* *Idris2 type-checking* — currently the Idris2 files define types but -are not compiled (no Idris2 in CI yet); adding `+idris2 --check+` to CI -is a tracked goal +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) Jonathan D.A. Jewell += Historical Connector ABI/FFI Design Note — 2026-03-01 +:toc: +:revdate: 2026-09-27 + +[WARNING] +==== +This dated note is retained as design history, not as a description of the +current connector implementations. Its original version assumed a uniform ABI +and test layout and listed `proven-authconn`, which is not present as a +maintained connector. Its claimed state machines, generated headers, proofs, +and test coverage were not established by this source inspection. +==== + +== Current source inventory + +The current top-level connector directories are `proven-cacheconn`, +`proven-dbconn`, `proven-nesy-solver-api`, `proven-queueconn`, +`proven-resolverconn`, and `proven-storageconn`. Their package contents differ; +not every one has the same Idris2 ABI, Zig FFI, C header, or executable tests. + +The NESY connector README describes its FFI dispatch operations as stubs and +its HTTP/Fly.io server as experimental. Its cloud deployment and image paths +are disabled. Other package-specific claims must be checked against current +source and tests. + +== Evidence boundary + +No repository-wide build, generated-header correspondence check, ABI matrix, or +connector interoperability test has been executed in the current assessment. +The root task loops require Idris2 and Zig, which were unavailable in the +assessment workspace. A type-level transition model, where present, would not +by itself establish that an independent native implementation enforces it. + +See `../../connectors/README.adoc`, `../../ABI-FFI-README.adoc`, +`../../.machine_readable/BINDINGS.a2ml`, `../../READINESS.adoc`, and +`../../PROOF-NEEDS.adoc` for current scope and verification needs. + +== Future design constraints + +For any connector that is promoted beyond prototype status: + +. Specify its intended package boundary and external contract. +. Identify the authoritative ABI source and make header generation or + comparison reproducible. +. Check integer widths, tag values, ownership, nullability, calling + conventions, error behavior, and resource lifetimes. +. Build and execute model, FFI, and boundary tests with recorded toolchains. +. Test real protocol behavior against independent fixtures or a peer. +. Keep unavailable integrations fail-closed; do not reuse another connector's + evidence or state table as proof. diff --git a/docs/maintenance/MAINTENANCE-CHECKLIST.adoc b/docs/maintenance/MAINTENANCE-CHECKLIST.adoc index a0f1d84f..c9617fd8 100644 --- a/docs/maintenance/MAINTENANCE-CHECKLIST.adoc +++ b/docs/maintenance/MAINTENANCE-CHECKLIST.adoc @@ -1,670 +1,140 @@ -== Maintenance Checklist (Cross-Repo) - -Use this as a repeatable maintenance runbook for any repo. - -Companion policy: - -* `+docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc+` (human-readable) -* `+.machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml+` -(machine-readable) - -=== Canonical Repo Baseline (Final) - -Apply this baseline to every repo unless an explicit exception is -recorded. - -==== Three-Axis Default Model - -* [ ] Axis 1 (scope priority, runs first): `+must > intend > like+` -* [ ] Axis 2 (maintenance priority): -`+corrective > adaptive > perfective+` -* [ ] Axis 3 (audit priority): `+systems > compliance > effects+` -* [ ] Perfective items are derived from Axis 1 honest state (not started -independently). - -==== Axis 1 Scoping Pass (Mandatory) - -Before Axis 2/3 execution, assemble a scoped worklist from evidence: - -* [ ] Read and reconcile: `+README+`, roadmap, status docs, maintenance -checklist, and current CI/security docs. -* [ ] Scan for unfinished markers: `+TODO+`, `+FIXME+`, `+XXX+`, -`+HACK+`, `+STUB+`, `+PARTIAL+`. -* [ ] If Idris is present, scan unsoundness markers: `+believe_me+`, -`+assert_total+`. -* [ ] Identify declared intent vs actual implementation (docs honesty -check). -* [ ] Produce a scope assembly artifact with prioritized entries under: -** `+must+` (release blockers / safety / correctness) -** `+intend+` (planned near-term) -** `+like+` (nice-to-have) - -==== Axis 2 Maintenance Execution Rules - -* [ ] Corrective first: fix breakage, defects, regressions, safety -issues. -* [ ] Adaptive second: reconcile changed scope, remove stale references, -cull no-longer-relevant work. -* [ ] Perfective third: only from current honest state established by -Axis 1 and updated by corrective/adaptive actions. - -==== Axis 3 Audit Rules - -* [ ] Verify systems are in place and actually operating. -* [ ] Verify documentation explains the real/current state (not -aspirational-only), including documented exceptions. -* [ ] Verify safety and security controls are present, active, and -evidenced. -* [ ] Verify observed effects/impacts are captured and reviewed. -* [ ] Effects audit includes: -** benchmark execution and recorded results (with before/after where -relevant) -** explicit maintainer dialogue/status review on what changed, why, and -next risks -* [ ] Audit compliance seams/compromises explicitly: -** policy exceptions are recorded with rationale, scope, and -expiry/review -** exception does not silently broaden into general policy drift -** language-policy contamination checks run (example: a single TS -exception must not trigger broad TypeScript conversion) -** run `+panic-attack+` as the compliance-audit scanner -** run ecological checking under effects (using sustainabot guidance as -current baseline) - -==== Generic Cleanup And Finish-Off Pass - -Run this pass at the end of a corrective/adaptive/perfective cycle: - -* [ ] Root cleanup: -** keep only required control/entry files in root -** move non-essential docs/reports/fixtures to canonical folders -* [ ] Remove or archive stale work: -** close out completed TODO/STUB/PARTIAL items -** cull obsolete references, dead files, and superseded plans -* [ ] Documentation finish-off: -** ensure README, roadmap, status, and wiki match actual implementation -state -** ensure machine-readable policy/state files match human docs -* [ ] Security/compliance finish-off: -** run compliance scanner (`+panic-attack+`) and resolve high-priority -findings -** verify exception register and seams/compromises are explicitly -bounded -* [ ] Effects finish-off: -** run benchmark/effects checks and record evidence -** conduct explicit maintainer review dialogue (what changed, why, -remaining risks) -* [ ] Release-prep finish-off: -** produce Must/Should/Could summary -** produce immediate corrective/adaptive/perfective next-actions list - -==== Must - -* [ ] Keep required control files at repository root: -** `+.gitignore+`, `+.gitattributes+`, `+.editorconfig+`, -`+.tool-versions+` -** `+Containerfile+` -** `+.containerignore+` (or `+.dockerignore+` only when required for -compatibility) -** `+CNAME+` and `+.nojekyll+` when using GitHub Pages/custom domain -** `+Justfile+` (root by convention) -* [ ] Keep ownership/governance files present: -** `+MAINTAINER+` in root -** `+.github/CODEOWNERS+` -* [ ] Keep machine-readable canonical structure under -`+.machine_readable/+`: -** state/meta/ecosystem files (`+*.a2ml+` or repo standard) -** `+anchors/ANCHOR.a2ml+` -** `+contractiles/+` (`+must+`, `+trust+`, `+lust+`, and related) -** `+ai/+` for AI guidance files -** `+bot_directives/+` for bot control files -* [ ] Keep contractiles/invariants present and wired: -** root `+Mustfile+` (or equivalent) with enforceable checks -** `+Trustfile+` and `+Intentfile+` present -* [ ] Keep security metadata present: -** `+.well-known/security.txt+` and relevant policy metadata -** CI security scanning configured and runnable -* [ ] Keep docs and navigation coherent: -** single navigation entry point in root (`+NAVIGATION.adoc+` or -equivalent) -** no duplicate conflicting docs for same purpose (for example both -`+.md+` and `+.adoc+` in root unless intentionally required) -* [ ] Enforce ABI/FFI purity where the policy applies: -** ABI definitions in Idris2 (`+src/abi/*.idr+`) -** FFI implementations in Zig (`+ffi/**/*.zig+`) -* [ ] Ensure quality gate includes: formatting, lint, unit/integration -tests, p2p/e2e checks, benchmark smoke, docs checks, security scan. - -==== Should - -* [ ] Keep human docs primarily in AsciiDoc (`+.adoc+`) except where -ecosystem rules require other formats (GitHub/community health, legal -text, tool-specific files). -* [ ] Keep non-essential root files moved into structured folders: -** `+docs/+` (theory/practice/whitepapers/proofs/reports) -** `+tests/+` (fixtures/outputs) -** `+licensing/+` (while retaining root `+LICENSE+` when forge detection -needs it) -* [ ] Maintain `+.well-known/+` for public metadata where applicable -(`+security.txt+`, `+humans.txt+`, `+ads.txt+` mirrors if used). -* [ ] Keep CI policy checks for doc-format conventions and canonical -file placement. -* [ ] Keep roadmap/status docs honest with dated evidence. - -==== Could - -* [ ] Maintain both human and machine views of maintenance policy from a -single source (generate one from the other). -* [ ] Add policy bots for corrective/adaptive/perfective/audit modes. -* [ ] Add repo-level architecture map (`+TOPOLOGY.md+`) and -release-readiness dashboards. -* [ ] Add per-repo exception registry for approved policy deviations. - -==== Explicit Root-Placement Rule - -Do *not* move the following out of root if you want default tool -behavior: - -* `+.gitignore+`, `+.gitattributes+`, `+.editorconfig+`, -`+.tool-versions+` -* `+Containerfile+` and ignore file -(`+.containerignore+`/`+.dockerignore+`) -* `+CNAME+` and `+.nojekyll+` for GitHub Pages -* `+Justfile+` - -=== Quick Automated Run (Script) - -Use the helper script first, then use the checklist for deeper/manual -follow-up. - -Script locations: - `+/var$REPOS_DIR/run-maintenance.sh+` - -`+~/Desktop/run-maintenance.sh+` - -[source,bash] ----- -~/Desktop/run-maintenance.sh --repo /absolute/path/to/repo --output /tmp/maintenance-report.json -jq . /tmp/maintenance-report.json ----- - -Useful flags: - -[source,bash] ----- -# Strict mode: fail process on failed checks -~/Desktop/run-maintenance.sh --repo /absolute/path/to/repo --strict - -# Skip expensive checks when needed -~/Desktop/run-maintenance.sh --repo /absolute/path/to/repo --skip-panic - -# Explicit language selection -~/Desktop/run-maintenance.sh --repo /absolute/path/to/repo --rust --python - -# Release hard-pass mode (fails on warnings or failures) -~/Desktop/run-maintenance.sh --repo /absolute/path/to/repo --fail-on-warn ----- - -Permission policy in script: - Flags `+g+w/o+w+` files/dirs - Flags -suspicious executable files - Flags shebang scripts missing executable -bit - Supports repo-local exceptions via `+.maintenance-perms-ignore+` -(regex per line) - *Audit-first by default* (non-mutating) - -`+--fix-perms+` is explicit opt-in only (never implicit) - For -reversible local hardening, pair snapshot/restore scripts where -available: - `+scripts/maintenance/perms-state.sh snapshot+` - -`+scripts/maintenance/perms-state.sh lock+` - -`+scripts/maintenance/perms-state.sh restore+` - -Important git behavior: - Git generally tracks execute bit, not full -UNIX mode matrix. - Permission hardening audits do not force -collaborators to re-unlock every file on pull. - Keep lock mode opt-in, -with restore path documented. - -[source,bash] ----- -# Audit-only (recommended default) -~/Desktop/run-maintenance.sh --repo /absolute/path/to/repo - -# Opt-in permission fixes (review output before commit) -~/Desktop/run-maintenance.sh --repo /absolute/path/to/repo --fix-perms ----- - -=== 0) Setup - -[source,bash] ----- -REPO="/absolute/path/to/repo" -cd "$REPO" ----- - -[source,bash] ----- -date -u -git rev-parse --abbrev-ref HEAD -git rev-parse HEAD -git status --porcelain ----- - -=== 1) Preflight - -* [ ] Confirm clean intent: note existing unrelated dirty files before -edits. -* [ ] Confirm runtime/toolchain versions. -* [ ] Confirm container mode expectation (`+podman+`/`+podman-compose+`) -if required. - -[source,bash] ----- -command -v rg git jq || true -command -v podman podman-compose || true ----- - -=== 2) Dependency/Env Prereqs - -* [ ] Python deps in active interpreter (for Python paths). -* [ ] Language-specific tooling installed. - -[source,bash] ----- -python -c "import sys; print(sys.executable)" -python -c "import pydantic; print(pydantic.__version__)" || echo "pydantic missing" ----- - -=== 3) Corrective Maintenance First - -* [ ] Fix regressions, runtime errors, panics, broken commands, failing -tests. -* [ ] Re-run failing checks immediately after each fix. - -=== 4) Code Health Scans - -* [ ] `+TODO/FIXME/XXX/HACK/STUB/PARTIAL+` scan. -* [ ] Permission policy scan (`+g+w/o+w+`, executable hygiene). -* [ ] ABI/FFI policy scan (if applicable: Idris2 ABI, Zig FFI). - -[source,bash] ----- -rg -n "TODO|FIXME|XXX|HACK|STUB|PARTIAL" -g '!**/.git/**' -g '!**/target/**' . ----- - -[source,bash] ----- -# Optional per-repo exceptions (regex per line): -# .maintenance-perms-ignore -# ^vendor/ -# ^third_party/ ----- - -[source,bash] ----- -# Adjust paths for your repo layout -find . -type f \( -name '*.idr' -o -name '*.idris2' -o -name '*.zig' \) ----- - -=== 5) Panic/Safety/Security Pass - -* [ ] Run `+panic-attacker+` assail/assault. -* [ ] Triage findings by severity. -* [ ] Fix high first, then medium. -* [ ] Re-run until acceptable. - -[source,bash] ----- -PANIC_BIN="/var$REPOS_DIR/panic-attacker/target/release/panic-attack" -"$PANIC_BIN" assail "$REPO" --output /tmp/assail.json --output-format json --quiet -jq -r '.weak_points | length' /tmp/assail.json -jq -r '.weak_points[] | "\(.severity)|\(.location)|\(.description)"' /tmp/assail.json ----- - -[source,bash] ----- -# If repo has production-only source builder, prefer this for baseline checks: -./scripts/ci/build-panic-assail-source.sh /tmp/panic-src -"$PANIC_BIN" assail /tmp/panic-src --output /tmp/assail-prod.json --output-format json --quiet ----- - -=== 6) Language-Specific Validation - -==== Rust - -* [ ] Format -* [ ] Lint -* [ ] Tests -* [ ] Doc tests -* [ ] Benches (where relevant) - -[source,bash] ----- -cargo fmt --all --check -cargo clippy --workspace --all-targets -- -D warnings -cargo test --workspace -cargo test --workspace --doc -# Optional targeted benchmarks: -cargo bench ----- - -==== Python - -* [ ] Format/lint -* [ ] Type check -* [ ] Tests - -[source,bash] ----- -ruff check . -ruff format --check . -mypy . -pytest -q ----- - -==== Elixir - -* [ ] Format check -* [ ] Lint/static checks -* [ ] Tests - -[source,bash] ----- -mix format --check-formatted -mix credo --strict -mix test ----- - -=== 7) Container/Runtime Checks (Podman) - -* [ ] Build container path. -* [ ] Run smoke tests inside containerized flow. -* [ ] Compare host vs container behavior for parity. - -[source,bash] ----- -podman --version -podman compose version || podman-compose --version ----- - -=== 8) Benchmark + Regression Check - -* [ ] Capture before/after metrics for touched hot paths. -* [ ] Record command + sample size + output. -* [ ] Fail change if critical path regresses beyond threshold. - -=== 9) Adaptive and Perfective Maintenance - -* [ ] Adaptive: compatibility updates (tooling/API/deprecations/config -flags). -* [ ] Perfective: clarity, docs parity, developer workflow improvements. -* [ ] Update roadmap/checklist/docs to match actual implementation -state. - -=== 10) Final QA and Release Hygiene - -* [ ] Re-run full relevant checks one final time. -* [ ] Confirm no unintended file changes. -* [ ] Commit scoped changes with clear message. -* [ ] Push and capture commit SHA. - -[source,bash] ----- -git status --short -git diff --stat -git add -git commit -m "maint: " -git push ----- - -=== 11) Maintenance Report Template - -Copy this block per repo run: - -[source,text] ----- -Repo: -Branch: -Start UTC: -End UTC: - -Scope: -- Corrective: -- Adaptive: -- Perfective: - -Checks Run: -- TODO/FIXME scan: -- Panic-attacker: -- Rust/Python/Elixir checks: -- Container checks: -- Benchmark checks: - -Findings: -- High: -- Medium: -- Low: - -Fixes Applied: -1. -2. -3. - -Validation Results: -- Tests: -- Benchmarks: -- Panic-attacker rerun: - -Artifacts: -- assail report: -- benchmark output: -- logs: - -Commit(s): -- SHA: - -Remaining Risks / Follow-ups: -1. -2. ----- - -=== 12) Language-Repo Additions (Eclexia-Specific) - -Add these checks for language/compiler repositories with formal ABI/FFI -constraints: - -* [x] README structure restored (index/TOC, audience paths, quickstart -sanity). -* [x] Wiki split by audience (laypeople/users/developers) and linked -from docs index. -* [x] Root-level clutter reduced (archive, analysis, reports relegated -to `+docs/+` subtrees). -* [x] Machine-readable docs synchronized (`+STATE.scm+`, `+META.scm+`, -`+ECOSYSTEM.scm+`, contractiles). -* [x] Human-readable docs synchronized (`+README+`, `+QUICK_STATUS+`, -roadmap, wiki home). -* [x] `+Mustfile+` invariants present and enforceable in CI. -* [x] `+Trustfile+` and `+Intentfile+` present and complete. -* [x] FFI/ABI purity policy enforced (`+*.zig+` for FFI, -`+*.idr+`/Idris2 for ABI). -* [x] `+panic-attack+` findings triaged with explicit severity budget -for release. -* [x] Point-to-point, end-to-end, and benchmark checks wired in one -quality gate. -* [x] CI workflows include quality + security + docs checks with -explicit policy. -* [x] Release audit includes corrective/adaptive/perfective + -Must/Should/Could. -* [x] Roadmap/status honesty pass completed (dates and current evidence -updated). - -=== 13) Latest Execution Record (Eclexia, 2026-02-24) - -Repo: `+/tmp/eclexia-releaseprep+` (branch `+release-prep+`, base -`+533ec9e9447f374135cc9e2e81021624ddb3c0ad+`) - -==== 13.1 Setup/Preflight - -* [x] Captured UTC timestamp and git state. -* [x] Tooling presence verified (`+rg+`, `+git+`, `+jq+`, `+cargo+`, -`+rustc+`, `+just+`). -* [x] Runtime/toolchain versions captured. -* [x] Container tooling checked (`+podman+`, `+podman-compose+`). - -==== 13.2 Corrective Maintenance - -* [x] Fixed `+panic-attack+` script path handling (`+mktemp+` output + -local fallback binary detection). -* [x] Removed Idris `+believe_me+` usage from ABI wrappers. -* [x] Fixed conformance crash-noise path by skipping known intentional -stack-overflow case in default runner. -* [x] Re-ran affected checks after each fix. - -==== 13.3 Code-Health Scans - -* [x] TODO/FIXME/STUB/PARTIAL scan run on active code paths. -* [x] ABI/FFI file inventory run (`+*.idr+`, `+*.zig+`). -* [x] Active-code marker count reduced/triaged; remaining items tracked -in release audit. - -==== 13.4 Security/Panic Pass - -* [x] `+panic-attack+` run and triaged. -* [x] Critical findings cleared (Idris unsoundness markers removed). -* [x] Current baseline: 0 weak points (Critical 0, High 0, Medium 0, Low -0). -* [x] High/Medium backlog fully eliminated. - -==== 13.5 Language Validation - -* [x] Final `+just quality-gate+` pass completed (docs, fmt, lint, unit, -conformance, integration, p2p, e2e, bench). -* [x] Additional targeted reruns completed (`+just test-conformance+`, -`+just panic-attack+`, `+just docs-check+`). - -==== 13.6 Adaptive/Perfective/Docs - -* [x] README/wiki/docs structure and indexing restored. -* [x] Root tidy/relegation pass executed. -* [x] Roadmap/status honesty update performed with current date and -evidence links. -* [x] Release audit created with corrective/adaptive/perfective + -Must/Should/Could. -* [x] Full quality-gate rerun passed after hardening updates. -* [x] ABI/FFI extension lane added without breaking stable symbols -(`+ecl_abi_get_info+`, `+ecl_tracker_create_ex+`, -`+ecl_tracker_snapshot+`). -* [x] CI quality workflow now validates sibling `+proven+` repo presence -and critical binding files. -* [x] Proven roadmap now includes explicit "`critical core, not full -rewrite`" adoption guidance and flowchart. - -==== 13.7 Outstanding Items (Explicit) - -* [x] Stable `+v1.0.0+` technical gate readiness met (quality + panic -scan clean). -* [x] Parser/codegen/runtime panic-path hardening completed for -scanner-flagged paths. -* [x] Non-eclexia `+proven+` library checked: already Idris2-first with -Zig ABI bridge; no additional integration changes required in this run. -* [ ] Remote push blocked by token scope: GitHub rejected branch updates -(`+release-prep+`, `+release-prep-pushable+`) due missing `+workflow+` -OAuth scope. - -==== 13.8 Artifacts - -* Release audit: `+docs/reports/V1-READINESS-AUDIT-2026-02-24.md+` -* Panic report: `+/tmp/eclexia-panic-attack.KZ1jpC.json+` (0 weak -points) -* Final quality gate log: `+/tmp/eclexia-quality-gate-final2.log+` (plus -post-change reruns via terminal sessions) -* Local commits: `+88fa2af+` (`+release-prep+`), `+baa3d1c+` -(`+release-prep-pushable+`) + pending new commit from this pass - -=== 12) LLM Operator Instructions - -Use this prompt with an LLM agent when you want the process run -end-to-end: - -[source,text] ----- -Run the maintenance workflow for this repo using MAINTENANCE-CHECKLIST.md. - -Required behavior: -1. Run ~/Desktop/run-maintenance.sh first and collect the JSON report. -2. Triage report results by severity: fail > warn > pass. -3. Execute corrective maintenance first (fix regressions, panics, broken tests/commands). -4. Run TODO/FIXME/stub scan and address relevant items. -5. Run panic-attacker and fix findings in priority order; rerun to confirm. -6. Run language-specific checks (Rust/Python/Elixir) relevant to this repo. -7. Run benchmark/regression checks for touched hot paths. -8. Enforce permission policy: - - no group/world writable source files unless justified - - executable bit only where intended - - use .maintenance-perms-ignore for justified exceptions -9. Update docs/roadmap/checklist entries to reflect actual state. -10. Produce a final report using the template in MAINTENANCE-CHECKLIST.md. - -Constraints: -- Do not revert unrelated existing dirty changes. -- Stage and commit only scoped intended files. -- If blocked, state exactly what is blocked and why. ----- - -=== 13) AI Execution Integrity Contract (Mandatory) - -Use this when delegating maintenance to any AI -(Gemini/Claude/ChatGPT/etc.). - -[source,text] ----- -You must execute this maintenance run with strict integrity. - -Non-negotiable rules: -1. Do not claim any step is complete unless you actually ran it. -2. Do not silently skip checklist items. If skipped, state SKIPPED + exact reason. -3. For every check, provide evidence: - - command executed - - pass/fail/warn - - key output summary - - artifact/log path -4. If a command fails, stop claiming success and report the failure clearly. -5. After each fix, re-run the relevant failing check and report the rerun result. -6. Do not hide uncertainty. If unsure, say so and run additional verification. -7. Never mark “all done” while any fail/warn remains unexplained. -8. Do not make destructive or broad permission changes by default. - - permission changes must be audit-first - - use --fix-perms only with explicit intent -9. Final output must include: - - checklist coverage matrix (each item: PASS/FAIL/WARN/SKIPPED) - - unresolved risks - - exact next actions -10. Prioritize user safety and reputation: no “looks fine” claims without evidence. ----- - -Recommended enforcement line for AI prompts: - -[source,text] ----- -Fail closed: if evidence is missing for any checklist item, treat that item as NOT DONE. ----- - -=== 14) Fleet Enrollment Automation (Gitbot + Hypatia) - -For centralized coverage across existing and new repos: - -[source,bash] ----- -cd /var$REPOS_DIR/gitbot-fleet -just enroll-repos ----- - -Optional directive write-back to repos that already have -`+.machine_readable/+`: - -[source,bash] ----- -cd /var$REPOS_DIR/gitbot-fleet -just enroll-repos /var$REPOS_DIR true ----- - -Release hard gate from fleet: - -[source,bash] ----- -cd /var$REPOS_DIR/gitbot-fleet -just maintenance-hard-pass /absolute/path/to/repo ----- +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Copyright (c) 2026 Jonathan D.A. Jewell += Maintenance Checklist — proven-servers +:revdate: 2026-09-27 +:toc: +:toclevels: 3 + +This repository-specific checklist applies the estate's maintenance baseline +without treating template-only files, stale audit reports, or unavailable tools +as evidence. The machine-readable counterpart is +`.machine_readable/policies/MAINTENANCE-CHECKLIST.a2ml`. + +== Three-axis order + +. *Scope first:* `must > intend > like`. +. *Maintenance next:* `corrective > adaptive > perfective`. +. *Audit last:* `systems > compliance > effects`. + +Perfective work starts only after the actual scope and corrective/adaptive +changes are understood. + +== Axis 1 — assemble scope + +Before changing code: + +* Read `README.adoc`, `ROADMAP.adoc`, `READINESS.adoc`, + `PROOF-NEEDS.adoc`, relevant package READMEs/manifests, current CI workflows, + and `SECURITY.adoc`. +* Search for `TODO`, `FIXME`, `XXX`, `HACK`, `STUB`, and `PARTIAL`; distinguish + active work from quoted examples, historical reports, and generated files. +* When reviewing Idris2, inspect `believe_me` and `assert_total` uses. A source + grep is a heuristic, not a proof of soundness. +* Reconcile implementation claims with the actual package manifests, test + targets, generated headers, and language bridges. +* Record work under Must, Intend, and Like, including evidence available, + skipped checks, and why they were skipped. + +== Axis 2 — maintain + +=== Must: corrective + +* Repair incorrect behavior, memory/ABI hazards, unsafe security success paths, + broken task recipes, and false user-facing claims. +* Do not report an unavailable or unimplemented authentication or cryptographic + operation as successful. +* Keep disabled bindings, container deployment, and unsupported package paths + clearly identified as unavailable. + +=== Intend: adaptive + +* Remove stale paths, counts, setup recipes, and status claims when the actual + repository layout or scope changes. +* Keep `.machine_readable/6a2/STATE.a2ml`, `META.a2ml`, `ECOSYSTEM.a2ml`, + `BINDINGS.a2ml`, `READINESS.adoc`, and `PROOF-NEEDS.adoc` in agreement. +* Keep root task definitions synchronized with + `.machine_readable/contractiles/Justfile`. +* Preserve historical audit reports as dated history; do not reuse them as + current build/test evidence. + +=== Like: perfective + +Only after corrective and adaptive work, consider formatter/linter adoption, +reproducible toolchain pins, test coverage, ABI generation, and benchmark +infrastructure. Do not label these items complete until the corresponding tools +and tests run. + +== Axis 3 — audit + +=== Systems + +* Run `just validate-rsr` for selected metadata paths and the synchronized task snapshot. +* Run `just test-static` for source-pattern and inventory smoke checks. +* Run `just build-idris`, `just build-zig`, and `just test-zig` with recorded + toolchains for compiler-backed checks. +* The static scripts are not formal proofs, exhaustive tests, security + certification, or ABI-conformance tests. + +=== Compliance and exceptions + +* `just assail` runs the external `panic-attack assail .` command only when + `panic-attack` is installed. Inspect the tool and record its exact version, + scope, and result; the missing-tool path fails explicitly. +* Apply estate standards through the repository's actual structure and the + standards applicability policy. Do not claim a capability solely to satisfy + a path gate that does not match this repository. +* Keep exceptions explicit, scoped, and reviewable; do not use broad language + bans that contradict the existing binding source inventory. + +=== Effects + +* Capture before/after benchmark or operational results when performance or + deployment effects are claimed. +* Include maintainer review of what changed, why, remaining risk, and next work. +* Do not claim ecological or deployment effects without an actual assessment. + +== Repository controls + +[cols="1,2,2"] +|=== +|Area |Current path/status |Check or limitation + +|Core metadata +|`.machine_readable/6a2/` and `anchor/ANCHOR.a2ml` +|`just validate-rsr`; selected paths only, not a full A2ML parser + +|Task runner +|Root `Justfile`; synchronized snapshot under `.machine_readable/contractiles/` +|`just fmt-check`; Git whitespace check only + +|Security contact +|`SECURITY.adoc` and `.well-known/security.txt` +|Keep URLs and expiry dates current + +|Language bindings +|20 language-named source directories +|Inventory only; native link/runtime support is not established + +|Build and test coverage +|Package-local Idris2 `.ipkg` and Zig `build.zig` manifests +|Requires `idris2` and `zig`; current status is in `READINESS.adoc` + +|Containers and deployment +|No root image/deployment stack; the experimental NESY connector's Fly.io and Containerfile configuration is disabled +|Do not build, sign, push, or deploy until an executable service and runtime tests exist + +|Quality-gate gaps +|No repository-wide formatter, full lint matrix, benchmark smoke, docs build, or binding matrix +|Do not represent these areas as passed +|=== + +== Finish-off checklist + +* [ ] Review the full diff and run `git diff --check`. +* [ ] Run all available checks; list unavailable tools and unrun tests explicitly. +* [ ] Confirm docs, machine metadata, and actual source agree. +* [ ] Confirm there are no unsupported deployment, binding, proof, or readiness + claims. +* [ ] Record current evidence in `READINESS.adoc` and `PROOF-NEEDS.adoc`. +* [ ] Produce a Must/Intend/Like summary and immediate corrective/adaptive/ + perfective next actions. diff --git a/docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc b/docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc index 7c43d562..ad120c41 100644 --- a/docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc +++ b/docs/practice/SOFTWARE-DEVELOPMENT-APPROACH.adoc @@ -1,65 +1,60 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 -// Copyright (c) Jonathan D.A. Jewell -= Software Development Approach (General) -:toc: left -:toclevels: 2 +// Copyright (c) 2026 Jonathan D.A. Jewell += Software Development Approach — proven-servers +:revdate: 2026-09-27 +:toc: -This is the general operating policy for software development across repositories. +This repository applies maintenance in a fixed order: scope, corrective/adaptive +work, then audit. This is a process guide; it does not assert that external +scanners, ecological analysis, or every package test are installed or have run. +The machine-readable counterpart is +`.machine_readable/policies/SOFTWARE-DEVELOPMENT-APPROACH.a2ml`. -== Core Sequence +== Axis 1: scope -Always run work in this order: +Priority: `must > intend > like`. -1. Scope first (Axis 1) -2. Maintenance second (Axis 2) -3. Audit third (Axis 3) +Build a scoped worklist from `README.adoc`, `ROADMAP.adoc`, `READINESS.adoc`, +`PROOF-NEEDS.adoc`, package READMEs and manifests, CI/security documentation, +and the source currently under review. Scan for unfinished markers and, where +Idris2 is present, proof escape-hatch identifiers. Check whether declared intent +matches actual implementations and tests. -== Axis Definitions +Output: a Must/Intend/Like assembly that records evidence, assumptions, skipped +checks, and rationale. -=== Axis 1: Scope +== Axis 2: maintenance -Priority order: `must > intend > like` +Priority: `corrective > adaptive > perfective`. -Axis 1 output is a scoped assembly of work based on: +* *Corrective:* repair concrete defects, broken checks, unsafe success paths, + and unsupported user-facing claims. +* *Adaptive:* reconcile changed scope, stale paths, obsolete tasks, machine and + human documentation, and explicit availability boundaries. +* *Perfective:* improve quality only after corrective/adaptive work establishes + the current honest state. -* README, roadmap, status, CI/security docs -* marker scans (`TODO`, `FIXME`, `XXX`, `HACK`, `STUB`, `PARTIAL`) -* Idris unsoundness scan when Idris exists (`believe_me`, `assert_total`) -* docs honesty check (intent vs actual implementation) +== Axis 3: audit -=== Axis 2: Maintenance +Priority: `systems > compliance > effects`. -Priority order: `corrective > adaptive > perfective` +* *Systems:* verify required mechanisms exist and actually operate. +* *Compliance:* record applicable requirements and scoped exceptions; use + standards capability gates only when repository paths/evidence satisfy them. +* *Effects:* capture benchmark and operational evidence when effects are + claimed, and review what changed, why, and remaining risks. -* Corrective: fix defects, regressions, breakage, security/safety failures -* Adaptive: reconcile scope changes, remove stale references, cull obsolete work -* Perfective: improve quality/clarity/performance only from the honest Axis 1 state +`panic-attack` and sustainabot-guided ecological checking are estate baselines +where applicable. They are not substitutes for the current configured tests, +and their results must not be claimed unless they were actually run. -=== Axis 3: Audit +== Finish-off -Priority order: `systems > compliance > effects` +At the end of a maintenance cycle: -* Systems: required mechanisms exist and are operating -* Compliance: seams/compromises/exceptions are explicit, bounded, and do not drift -* Effects: benchmark and operational impact evidence is captured and reviewed - -Compliance scanner baseline: `panic-attack` + -Effects/ecological baseline: sustainabot-guided ecological checking - -== Generic Cleanup And Finish-Off - -At cycle end: - -* reduce root clutter to required control/entry files -* archive/remove stale or superseded work -* synchronize human and machine docs -* run compliance and effects audits with evidence capture -* produce Must/Should/Could summary and immediate next-actions list - -== Collaboration Rule - -Effects review must include explicit maintainer dialogue: - -* what changed -* why it changed -* what risks remain +* remove stale claims and unsafe or misleading no-op tasks; +* keep state, registry, readiness, and proof-needs metadata synchronized; +* run available checks and report tool-dependent checks that remain unrun; +* avoid promoting a source inventory, smoke check, or historical audit into + build, proof, conformance, or deployment evidence; +* record Must/Intend/Like and immediate corrective/adaptive/perfective actions. diff --git a/ffi/zig/build.zig b/ffi/zig/build.zig index 4a6b3e5e..613a500d 100644 --- a/ffi/zig/build.zig +++ b/ffi/zig/build.zig @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Jonathan D.A. Jewell -// PROVEN_SERVERS FFI Build Configuration +// +// Build configuration for the root-level C-ABI prototype. +// This example API is not a protocol server or a repository-wide binding. const std = @import("std"); @@ -8,88 +10,42 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - // Shared library (.so, .dylib, .dll) - const lib = b.addSharedLibrary(.{ - .name = "proven_servers", + const shared_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); - - // Set version - lib.version = .{ .major = 0, .minor = 1, .patch = 0 }; - - // Static library (.a) - const lib_static = b.addStaticLibrary(.{ + const shared_lib = b.addLibrary(.{ .name = "proven_servers", - .root_source_file = b.path("src/main.zig"), - .target = target, - .optimize = optimize, + .root_module = shared_module, + .linkage = .dynamic, }); + b.installArtifact(shared_lib); - // Install artifacts - b.installArtifact(lib); - b.installArtifact(lib_static); - - // Generate header file for C compatibility - const header = b.addInstallHeader( - b.path("include/proven_servers.h"), - "proven_servers.h", - ); - b.getInstallStep().dependOn(&header.step); - - // Unit tests - const lib_tests = b.addTest(.{ + const static_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); - - const run_lib_tests = b.addRunArtifact(lib_tests); - - const test_step = b.step("test", "Run library tests"); - test_step.dependOn(&run_lib_tests.step); - - // Integration tests - const integration_tests = b.addTest(.{ - .root_source_file = b.path("test/integration_test.zig"), - .target = target, - .optimize = optimize, + const static_lib = b.addLibrary(.{ + .name = "proven_servers", + .root_module = static_module, + .linkage = .static, }); + b.installArtifact(static_lib); - integration_tests.linkLibrary(lib); - - const run_integration_tests = b.addRunArtifact(integration_tests); - - const integration_test_step = b.step("test-integration", "Run integration tests"); - integration_test_step.dependOn(&run_integration_tests.step); - - // Documentation - const docs = b.addTest(.{ + const api_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = target, - .optimize = .Debug, + .optimize = optimize, }); - - const docs_step = b.step("docs", "Generate documentation"); - docs_step.dependOn(&b.addInstallDirectory(.{ - .source_dir = docs.getEmittedDocs(), - .install_dir = .prefix, - .install_subdir = "docs", - }).step); - - // Benchmark (if needed) - const bench = b.addExecutable(.{ - .name = "proven_servers-bench", - .root_source_file = b.path("bench/bench.zig"), + const test_module = b.createModule(.{ + .root_source_file = b.path("test/integration_test.zig"), .target = target, - .optimize = .ReleaseFast, + .optimize = optimize, + .imports = &.{.{ .name = "proven_servers", .module = api_module }}, }); - - bench.linkLibrary(lib); - - const run_bench = b.addRunArtifact(bench); - - const bench_step = b.step("bench", "Run benchmarks"); - bench_step.dependOn(&run_bench.step); + const tests = b.addTest(.{ .root_module = test_module }); + const run_tests = b.addRunArtifact(tests); + b.step("test", "Run root C-ABI prototype tests").dependOn(&run_tests.step); } diff --git a/ffi/zig/src/main.zig b/ffi/zig/src/main.zig index bce38d3f..b026f841 100644 --- a/ffi/zig/src/main.zig +++ b/ffi/zig/src/main.zig @@ -1,23 +1,25 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Jonathan D.A. Jewell -// PROVEN_SERVERS FFI Implementation +// Generic root-level C-ABI example scaffold. // -// This module implements the C-compatible FFI declared in src/abi/Foreign.idr -// All types and layouts must match the Idris2 ABI definitions. +// It does not implement a protocol or establish conformance to the separate +// Idris2 model. It exists only as a small buildable FFI prototype. // const std = @import("std"); // Version information (keep in sync with project) const VERSION = "0.1.0"; -const BUILD_INFO = "PROVEN_SERVERS built with Zig " ++ @import("builtin").zig_version_string; +const BUILD_INFO = "proven-servers FFI prototype built with Zig " ++ @import("builtin").zig_version_string; +const EXAMPLE_RESULT: [:0]const u8 = "Example result"; +const MAX_PROCESS_ARRAY_LEN: u32 = 1_048_576; -/// Thread-local error storage -threadlocal var last_error: ?[]const u8 = null; +/// Thread-local pointer to a static, sentinel-terminated error message. +threadlocal var last_error: ?[*:0]const u8 = null; -/// Set the last error message -fn setError(msg: []const u8) void { - last_error = msg; +/// Set the last error message without allocating or transferring ownership. +fn setError(msg: [:0]const u8) void { + last_error = msg.ptr; } /// Clear the last error @@ -29,7 +31,7 @@ fn clearError() void { // Core Types (must match src/abi/Types.idr) //============================================================================== -/// Result codes (must match Idris2 Result type) +/// Result codes for this prototype; no Idris2 ABI conformance is asserted. pub const Result = enum(c_int) { ok = 0, @"error" = 1, @@ -38,14 +40,19 @@ pub const Result = enum(c_int) { null_pointer = 4, }; -/// Library handle (opaque to prevent direct access) -pub const Handle = opaque { - // Internal state hidden from C +/// Opaque handle type exposed across the C ABI. +pub const Handle = opaque {}; + +/// Private state behind the opaque ABI handle. +const HandleState = struct { allocator: std.mem.Allocator, initialized: bool, - // Add your fields here }; +fn stateOf(handle: *Handle) *HandleState { + return @ptrCast(@alignCast(handle)); +} + //============================================================================== // Library Lifecycle //============================================================================== @@ -53,32 +60,26 @@ pub const Handle = opaque { /// Initialize the library /// Returns a handle, or null on failure export fn proven_servers_init() ?*Handle { - const allocator = std.heap.c_allocator; + const allocator = std.heap.page_allocator; - const handle = allocator.create(Handle) catch { + const state = allocator.create(HandleState) catch { setError("Failed to allocate handle"); return null; }; - - // Initialize handle - handle.* = .{ - .allocator = allocator, - .initialized = true, - }; + state.* = .{ .allocator = allocator, .initialized = true }; clearError(); - return handle; + return @ptrCast(state); } -/// Free the library handle +/// Free a handle exactly once. Passing null is a no-op; reusing a freed handle +/// is invalid and cannot be made safe by this raw-pointer ABI. export fn proven_servers_free(handle: ?*Handle) void { const h = handle orelse return; - const allocator = h.allocator; - - // Clean up resources - h.initialized = false; - - allocator.destroy(h); + const state = stateOf(h); + const allocator = state.allocator; + state.initialized = false; + allocator.destroy(state); clearError(); } @@ -92,13 +93,14 @@ export fn proven_servers_process(handle: ?*Handle, input: u32) Result { setError("Null handle"); return .null_pointer; }; + const state = stateOf(h); - if (!h.initialized) { + if (!state.initialized) { setError("Handle not initialized"); return .@"error"; } - // Example processing logic + // This prototype intentionally does not implement protocol processing. _ = input; clearError(); @@ -109,36 +111,24 @@ export fn proven_servers_process(handle: ?*Handle, input: u32) Result { // String Operations //============================================================================== -/// Get a string result (example) -/// Caller must free the returned string +/// Return a static example string. This prototype does not return protocol data. export fn proven_servers_get_string(handle: ?*Handle) ?[*:0]const u8 { const h = handle orelse { setError("Null handle"); return null; }; - - if (!h.initialized) { + if (!stateOf(h).initialized) { setError("Handle not initialized"); return null; } - // Example: allocate and return a string - const result = h.allocator.dupeZ(u8, "Example result") catch { - setError("Failed to allocate string"); - return null; - }; - clearError(); - return result.ptr; + return EXAMPLE_RESULT.ptr; } -/// Free a string allocated by the library +/// Compatibility no-op for the static string returned above. export fn proven_servers_free_string(str: ?[*:0]const u8) void { - const s = str orelse return; - const allocator = std.heap.c_allocator; - - const slice = std.mem.span(s); - allocator.free(slice); + _ = str; } //============================================================================== @@ -155,23 +145,21 @@ export fn proven_servers_process_array( setError("Null handle"); return .null_pointer; }; - - const buf = buffer orelse { - setError("Null buffer"); - return .null_pointer; - }; - - if (!h.initialized) { + if (!stateOf(h).initialized) { setError("Handle not initialized"); return .@"error"; } + if (len > MAX_PROCESS_ARRAY_LEN) { + setError("Input length exceeds prototype limit"); + return .invalid_param; + } + if (len > 0 and buffer == null) { + setError("Null buffer"); + return .null_pointer; + } - // Access the buffer - const data = buf[0..len]; - _ = data; - - // Process data here - + // This example validates the declared length but intentionally does not + // read caller-owned memory or implement protocol processing. clearError(); return .ok; } @@ -183,12 +171,7 @@ export fn proven_servers_process_array( /// Get the last error message /// Returns null if no error export fn proven_servers_last_error() ?[*:0]const u8 { - const err = last_error orelse return null; - - // Return C string (static storage, no need to free) - const allocator = std.heap.c_allocator; - const c_str = allocator.dupeZ(u8, err) catch return null; - return c_str.ptr; + return last_error; } //============================================================================== @@ -210,7 +193,7 @@ export fn proven_servers_build_info() [*:0]const u8 { //============================================================================== /// Callback function type (C ABI) -pub const Callback = *const fn (u64, u32) callconv(.C) u32; +pub const Callback = *const fn (u64, u32) callconv(.c) u32; /// Register a callback export fn proven_servers_register_callback( @@ -227,12 +210,12 @@ export fn proven_servers_register_callback( return .null_pointer; }; - if (!h.initialized) { + if (!stateOf(h).initialized) { setError("Handle not initialized"); return .@"error"; } - // Store callback for later use + // This prototype validates the callback argument but does not retain it. _ = cb; clearError(); @@ -246,7 +229,7 @@ export fn proven_servers_register_callback( /// Check if handle is initialized export fn proven_servers_is_initialized(handle: ?*Handle) u32 { const h = handle orelse return 0; - return if (h.initialized) 1 else 0; + return if (stateOf(h).initialized) 1 else 0; } //============================================================================== diff --git a/ffi/zig/test/integration_test.zig b/ffi/zig/test/integration_test.zig index 8277973a..2956032c 100644 --- a/ffi/zig/test/integration_test.zig +++ b/ffi/zig/test/integration_test.zig @@ -1,183 +1,102 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Jonathan D.A. Jewell -// PROVEN_SERVERS Integration Tests // -// These tests verify that the Zig FFI correctly implements the Idris2 ABI +// Tests for the generic root-level FFI prototype. These call the Zig module +// directly; they do not test a compiled C consumer or establish a protocol ABI. const std = @import("std"); const testing = std.testing; +const proven_servers = @import("proven_servers"); -// Import FFI functions -extern fn proven_servers_init() ?*opaque {}; -extern fn proven_servers_free(?*opaque {}) void; -extern fn proven_servers_process(?*opaque {}, u32) c_int; -extern fn proven_servers_get_string(?*opaque {}) ?[*:0]const u8; -extern fn proven_servers_free_string(?[*:0]const u8) void; -extern fn proven_servers_last_error() ?[*:0]const u8; -extern fn proven_servers_version() [*:0]const u8; -extern fn proven_servers_is_initialized(?*opaque {}) u32; - -//============================================================================== -// Lifecycle Tests -//============================================================================== +// Lifecycle ----------------------------------------------------------------- test "create and destroy handle" { - const handle = proven_servers_init() orelse return error.InitFailed; - defer proven_servers_free(handle); - - try testing.expect(handle != null); -} - -test "handle is initialized" { - const handle = proven_servers_init() orelse return error.InitFailed; - defer proven_servers_free(handle); - - const initialized = proven_servers_is_initialized(handle); - try testing.expectEqual(@as(u32, 1), initialized); + const handle = proven_servers.proven_servers_init() orelse return error.InitFailed; + defer proven_servers.proven_servers_free(handle); + try testing.expectEqual(@as(u32, 1), proven_servers.proven_servers_is_initialized(handle)); } test "null handle is not initialized" { - const initialized = proven_servers_is_initialized(null); - try testing.expectEqual(@as(u32, 0), initialized); + try testing.expectEqual(@as(u32, 0), proven_servers.proven_servers_is_initialized(null)); } -//============================================================================== -// Operation Tests -//============================================================================== - -test "process with valid handle" { - const handle = proven_servers_init() orelse return error.InitFailed; - defer proven_servers_free(handle); - - const result = proven_servers_process(handle, 42); - try testing.expectEqual(@as(c_int, 0), result); // 0 = ok -} - -test "process with null handle returns error" { - const result = proven_servers_process(null, 42); - try testing.expectEqual(@as(c_int, 4), result); // 4 = null_pointer +test "free null is safe" { + proven_servers.proven_servers_free(null); } -//============================================================================== -// String Tests -//============================================================================== +// Example operations --------------------------------------------------------- -test "get string result" { - const handle = proven_servers_init() orelse return error.InitFailed; - defer proven_servers_free(handle); - - const str = proven_servers_get_string(handle); - defer if (str) |s| proven_servers_free_string(s); - - try testing.expect(str != null); +test "process accepts a live handle as a no-op example" { + const handle = proven_servers.proven_servers_init() orelse return error.InitFailed; + defer proven_servers.proven_servers_free(handle); + try testing.expectEqual(proven_servers.Result.ok, proven_servers.proven_servers_process(handle, 42)); } -test "get string with null handle" { - const str = proven_servers_get_string(null); - try testing.expect(str == null); +test "process rejects a null handle" { + try testing.expectEqual(proven_servers.Result.null_pointer, proven_servers.proven_servers_process(null, 42)); + try testing.expect(proven_servers.proven_servers_last_error() != null); } -//============================================================================== -// Error Handling Tests -//============================================================================== - -test "last error after null handle operation" { - _ = proven_servers_process(null, 0); - - const err = proven_servers_last_error(); - try testing.expect(err != null); - - if (err) |e| { - const err_str = std.mem.span(e); - try testing.expect(err_str.len > 0); - } +test "process array bounds its declared length" { + const handle = proven_servers.proven_servers_init() orelse return error.InitFailed; + defer proven_servers.proven_servers_free(handle); + + try testing.expectEqual( + proven_servers.Result.null_pointer, + proven_servers.proven_servers_process_array(handle, null, 1), + ); + try testing.expectEqual( + proven_servers.Result.invalid_param, + proven_servers.proven_servers_process_array(handle, null, 1_048_577), + ); + try testing.expectEqual( + proven_servers.Result.ok, + proven_servers.proven_servers_process_array(handle, null, 0), + ); } -test "no error after successful operation" { - const handle = proven_servers_init() orelse return error.InitFailed; - defer proven_servers_free(handle); +test "get string returns a static example value" { + const handle = proven_servers.proven_servers_init() orelse return error.InitFailed; + defer proven_servers.proven_servers_free(handle); - _ = proven_servers_process(handle, 0); + const result = proven_servers.proven_servers_get_string(handle) orelse return error.MissingResult; + try testing.expectEqualStrings("Example result", std.mem.span(result)); + proven_servers.proven_servers_free_string(result); +} - // Error should be cleared after successful operation - // (This depends on implementation) +test "get string rejects a null handle" { + try testing.expect(proven_servers.proven_servers_get_string(null) == null); } -//============================================================================== -// Version Tests -//============================================================================== +// Error and version reporting ----------------------------------------------- -test "version string is not empty" { - const ver = proven_servers_version(); - const ver_str = std.mem.span(ver); +test "successful operation clears the thread-local error" { + _ = proven_servers.proven_servers_process(null, 0); + try testing.expect(proven_servers.proven_servers_last_error() != null); - try testing.expect(ver_str.len > 0); + const handle = proven_servers.proven_servers_init() orelse return error.InitFailed; + defer proven_servers.proven_servers_free(handle); + _ = proven_servers.proven_servers_process(handle, 0); + try testing.expect(proven_servers.proven_servers_last_error() == null); } -test "version string is semantic version format" { - const ver = proven_servers_version(); - const ver_str = std.mem.span(ver); - - // Should be in format X.Y.Z - try testing.expect(std.mem.count(u8, ver_str, ".") >= 1); +test "version string is non-empty" { + try testing.expect(std.mem.span(proven_servers.proven_servers_version()).len > 0); } -//============================================================================== -// Memory Safety Tests -//============================================================================== - -test "multiple handles are independent" { - const h1 = proven_servers_init() orelse return error.InitFailed; - defer proven_servers_free(h1); - - const h2 = proven_servers_init() orelse return error.InitFailed; - defer proven_servers_free(h2); - - try testing.expect(h1 != h2); - - // Operations on h1 should not affect h2 - _ = proven_servers_process(h1, 1); - _ = proven_servers_process(h2, 2); +test "build information is non-empty" { + try testing.expect(std.mem.span(proven_servers.proven_servers_build_info()).len > 0); } -test "double free is safe" { - const handle = proven_servers_init() orelse return error.InitFailed; - - proven_servers_free(handle); - proven_servers_free(handle); // Should not crash -} +// Handle separation ---------------------------------------------------------- -test "free null is safe" { - proven_servers_free(null); // Should not crash -} +test "multiple handles have distinct addresses" { + const first = proven_servers.proven_servers_init() orelse return error.InitFailed; + defer proven_servers.proven_servers_free(first); + const second = proven_servers.proven_servers_init() orelse return error.InitFailed; + defer proven_servers.proven_servers_free(second); -//============================================================================== -// Thread Safety Tests (if applicable) -//============================================================================== - -test "concurrent operations" { - const handle = proven_servers_init() orelse return error.InitFailed; - defer proven_servers_free(handle); - - const ThreadContext = struct { - h: *opaque {}, - id: u32, - }; - - const thread_fn = struct { - fn run(ctx: ThreadContext) void { - _ = proven_servers_process(ctx.h, ctx.id); - } - }.run; - - var threads: [4]std.Thread = undefined; - for (&threads, 0..) |*thread, i| { - thread.* = try std.Thread.spawn(.{}, thread_fn, .{ - ThreadContext{ .h = handle, .id = @intCast(i) }, - }); - } - - for (threads) |thread| { - thread.join(); - } + try testing.expect(first != second); + try testing.expectEqual(proven_servers.Result.ok, proven_servers.proven_servers_process(first, 1)); + try testing.expectEqual(proven_servers.Result.ok, proven_servers.proven_servers_process(second, 2)); } diff --git a/llm-warmup-dev.adoc b/llm-warmup-dev.adoc index 989a76f2..7ae33593 100644 --- a/llm-warmup-dev.adoc +++ b/llm-warmup-dev.adoc @@ -1,19 +1,25 @@ -== LLM Warmup — proven-servers (Developer) += LLM Warmup — proven-servers Developer -=== What is proven-servers? +== Scope -See README.adoc for overview. +`proven-servers` is a research/prototype source monorepo, not a deployable +server suite. Start with `0-AI-MANIFEST.a2ml`, `README.adoc`, and +`.machine_readable/6a2/STATE.a2ml`. -=== Key Commands +== Configured commands -* `+just setup+` — set up development environment -* `+just build+` — build the project -* `+just test+` — run tests -* `+just doctor+` — diagnose issues -* `+just heal+` — attempt auto-repair +* `just info` — show project phase and readiness. +* `just deps` — report whether Just, Idris2, and Zig are available; install + nothing. +* `just test-static` — source-pattern, security-heuristic, and binding + inventory checks only. +* `just build-idris`, `just build-zig`, `just test-zig` — compiler-backed + package checks; require the corresponding tools. +* `just e2e` — bounded selected-package sweep, not full network E2E or + cross-language conformance. -=== Quick Context - -* License: MPL-2.0 -* Part of hyperpolymath ecosystem -* See EXPLAINME.adoc for architecture +There is no `just setup`, `heal`, `install`, release, or deployment recipe. +`just doctor` reports tool availability and Git state only; `just assail` invokes +an optional external scanner. Static checks and historical audit reports are +not proof or runtime evidence. Record skipped tools and limitations in any +handoff. diff --git a/llm-warmup-user.adoc b/llm-warmup-user.adoc index cf6c18c3..72714a85 100644 --- a/llm-warmup-user.adoc +++ b/llm-warmup-user.adoc @@ -1,19 +1,19 @@ -== LLM Warmup — proven-servers (User) += LLM Warmup — proven-servers Reader -=== What is proven-servers? +== Scope -See README.adoc for overview. +This repository contains research/prototype source, protocol models, FFI +experiments, and binding scaffolds. It does not currently provide an +end-user server or supported deployment. Read `README.adoc`, `READINESS.adoc`, +and the package-specific README before relying on any component. -=== Key Commands +== Useful references -* `+just setup+` — set up development environment -* `+just build+` — build the project -* `+just test+` — run tests -* `+just doctor+` — diagnose issues -* `+just heal+` — attempt auto-repair +* `QUICKSTART-USER.adoc` — what readers can and cannot run. +* `QUICKSTART-DEV.adoc` — package build and test instructions. +* `SECURITY.adoc` — vulnerability reporting and security scope. -=== Quick Context - -* License: MPL-2.0 -* Part of hyperpolymath ecosystem -* See EXPLAINME.adoc for architecture +There is no supported `just setup`, `just run`, or `just install` path. +`just doctor` reports availability/state only; it does not install or repair +anything. Directory presence and static smoke tests do not establish server or +protocol support. diff --git a/not-proven/proven-container/src/Main.idr b/not-proven/proven-container/src/Main.idr index f060d3db..572119cd 100644 --- a/not-proven/proven-container/src/Main.idr +++ b/not-proven/proven-container/src/Main.idr @@ -9,7 +9,6 @@ import Container %default total ||| Print server name, ports, and enumerate all type constructors. -partial main : IO () main = do putStrLn "==========================================" diff --git a/protocols/proven-authserver/ffi/zig/src/authserver.zig b/protocols/proven-authserver/ffi/zig/src/authserver.zig index 566671d0..4d292ba6 100644 --- a/protocols/proven-authserver/ffi/zig/src/authserver.zig +++ b/protocols/proven-authserver/ffi/zig/src/authserver.zig @@ -265,31 +265,19 @@ pub export fn authserver_authenticate(slot: c_int, method: u8) callconv(.c) u8 { } if (method > 7) return @intFromEnum(AuthResult.invalid_credentials); - // Check if already locked out from too many failures + // This ABI carries no credential material and has no authenticator. A + // matching method tag is not proof of identity, so every attempt fails. if (sessions[idx].failed_attempts >= MAX_FAILED_ATTEMPTS) { sessions[idx].state = .locked; return @intFromEnum(AuthResult.account_locked); } - const req_method: AuthMethod = @enumFromInt(method); - - // Simulate auth: method must match session's configured method - if (req_method != sessions[idx].auth_method) { - sessions[idx].failed_attempts += 1; - if (sessions[idx].failed_attempts >= MAX_FAILED_ATTEMPTS) { - sessions[idx].state = .locked; - return @intFromEnum(AuthResult.account_locked); - } - return @intFromEnum(AuthResult.invalid_credentials); - } - - // Check if MFA is required and not yet verified - if (sessions[idx].mfa_required and !sessions[idx].mfa_verified) { - return @intFromEnum(AuthResult.mfa_required); + sessions[idx].failed_attempts += 1; + if (sessions[idx].failed_attempts >= MAX_FAILED_ATTEMPTS) { + sessions[idx].state = .locked; + return @intFromEnum(AuthResult.account_locked); } - - sessions[idx].auth_count += 1; - return @intFromEnum(AuthResult.success); + return @intFromEnum(AuthResult.invalid_credentials); } // -- MFA ---------------------------------------------------------------------- @@ -309,21 +297,12 @@ pub export fn authserver_require_mfa(slot: c_int, mfa_method: u8) callconv(.c) u return 0; } -/// Verify MFA challenge. Returns 0 on success, 1 on rejection. +/// Verify MFA challenge. The ABI accepts no challenge/response material, so +/// the method tag alone cannot verify a factor. Always rejects without mutation. pub export fn authserver_verify_mfa(slot: c_int, mfa_method: u8) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - - const idx = validSlot(slot) orelse return 1; - if (sessions[idx].state != .active) return 1; - if (!sessions[idx].mfa_required) return 1; - if (mfa_method > 4) return 1; - - const req_mfa: MFAMethod = @enumFromInt(mfa_method); - if (req_mfa != sessions[idx].mfa_method) return 1; - - sessions[idx].mfa_verified = true; - return 0; + _ = slot; + _ = mfa_method; + return 1; } // -- Token management --------------------------------------------------------- @@ -337,6 +316,10 @@ pub export fn authserver_issue_token(slot: c_int, token_type: u8) callconv(.c) u const idx = validSlot(slot) orelse return 1; if (sessions[idx].state != .active) return 1; if (token_type > 3) return 1; + // This model has no authenticator, so auth_count remains zero and token + // issuance fails closed until a reviewed identity backend is integrated. + if (sessions[idx].auth_count == 0) return 1; + if (sessions[idx].mfa_required and !sessions[idx].mfa_verified) return 1; // Find free token slot for (&sessions[idx].tokens) |*t| { diff --git a/protocols/proven-authserver/ffi/zig/test/integration_test.zig b/protocols/proven-authserver/ffi/zig/test/integration_test.zig index 0eaca61c..e208fb4c 100644 --- a/protocols/proven-authserver/ffi/zig/test/integration_test.zig +++ b/protocols/proven-authserver/ffi/zig/test/integration_test.zig @@ -98,11 +98,12 @@ test "destroy is safe with invalid slot" { // Authentication // ========================================================================= -test "authenticate succeeds with matching method" { +test "method tag without credentials never authenticates" { const slot = authserver.authserver_create(0); // Password defer authserver.authserver_destroy(slot); - try std.testing.expectEqual(@as(u8, 0), authserver.authserver_authenticate(slot, 0)); // Success + try std.testing.expectEqual(@as(u8, 1), authserver.authserver_authenticate(slot, 0)); // InvalidCredentials + try std.testing.expectEqual(@as(u32, 1), authserver.authserver_failed_attempts(slot)); } test "authenticate fails with wrong method" { @@ -139,21 +140,21 @@ test "failed_attempts tracks count" { // MFA workflow // ========================================================================= -test "require_mfa then authenticate returns MFARequired" { +test "configured MFA cannot turn a method tag into authentication" { const slot = authserver.authserver_create(0); // Password defer authserver.authserver_destroy(slot); - try std.testing.expectEqual(@as(u8, 0), authserver.authserver_require_mfa(slot, 0)); // TOTP - try std.testing.expectEqual(@as(u8, 4), authserver.authserver_authenticate(slot, 0)); // MFARequired + try std.testing.expectEqual(@as(u8, 0), authserver.authserver_require_mfa(slot, 0)); // TOTP policy + try std.testing.expectEqual(@as(u8, 1), authserver.authserver_authenticate(slot, 0)); // InvalidCredentials } -test "verify_mfa then authenticate succeeds" { +test "MFA verification fails without challenge material" { const slot = authserver.authserver_create(0); // Password defer authserver.authserver_destroy(slot); _ = authserver.authserver_require_mfa(slot, 0); // TOTP - try std.testing.expectEqual(@as(u8, 0), authserver.authserver_verify_mfa(slot, 0)); // TOTP - try std.testing.expectEqual(@as(u8, 0), authserver.authserver_authenticate(slot, 0)); // Success + try std.testing.expectEqual(@as(u8, 1), authserver.authserver_verify_mfa(slot, 0)); + try std.testing.expectEqual(@as(u32, 0), authserver.authserver_token_count(slot)); } test "verify_mfa rejects wrong method" { @@ -175,13 +176,13 @@ test "verify_mfa rejects when not required" { // Token management // ========================================================================= -test "issue_token increments count" { +test "issue_token fails closed without successful authentication" { const slot = authserver.authserver_create(0); defer authserver.authserver_destroy(slot); - try std.testing.expectEqual(@as(u8, 0), authserver.authserver_issue_token(slot, 0)); // Access - try std.testing.expectEqual(@as(u8, 0), authserver.authserver_issue_token(slot, 1)); // Refresh - try std.testing.expectEqual(@as(u32, 2), authserver.authserver_token_count(slot)); + try std.testing.expectEqual(@as(u8, 1), authserver.authserver_issue_token(slot, 0)); // Access + try std.testing.expectEqual(@as(u8, 1), authserver.authserver_issue_token(slot, 1)); // Refresh + try std.testing.expectEqual(@as(u32, 0), authserver.authserver_token_count(slot)); } test "issue_token rejects invalid type" { diff --git a/protocols/proven-authserver/proven-authserver.ipkg b/protocols/proven-authserver/proven-authserver.ipkg index 8b62b6e5..1222f84e 100644 --- a/protocols/proven-authserver/proven-authserver.ipkg +++ b/protocols/proven-authserver/proven-authserver.ipkg @@ -5,7 +5,7 @@ package proven-authserver version = "0.1.0" authors = "Jonathan D.A. Jewell" license = "MPL-2.0" -brief = "Authentication server -- provably correct credential validation" +brief = "Authentication lifecycle model; credential, MFA, and token operations fail closed" sourcedir = "src" main = Main diff --git a/protocols/proven-authserver/src/AuthserverABI/Foreign.idr b/protocols/proven-authserver/src/AuthserverABI/Foreign.idr index 567cb124..476f6d9f 100644 --- a/protocols/proven-authserver/src/AuthserverABI/Foreign.idr +++ b/protocols/proven-authserver/src/AuthserverABI/Foreign.idr @@ -3,16 +3,10 @@ -- -- AuthserverABI.Foreign: Foreign function declarations for the C bridge. -- --- Declares the opaque handle type and documents the complete FFI contract --- that the Zig implementation (ffi/zig/src/authserver.zig) must provide. --- --- The Zig FFI manages: --- - 64-slot mutex-protected session pool --- - Authentication attempts with configurable methods --- - MFA challenge/response workflow --- - Token issuance and revocation --- - Session lifecycle (Active -> Expired/Revoked/Locked) --- - Failed attempt tracking with lockout +-- Declares the opaque handle type and documents the session lifecycle model. +-- The ABI carries no credentials or MFA challenge material, so authentication +-- and MFA always reject; token issuance fails closed without authenticated state. +-- The Active tag denotes a live context, not an authenticated identity. -- -- All functions use C calling convention and communicate state via -- Bits8 tags matching AuthserverABI.Types exactly. @@ -52,7 +46,7 @@ abiVersion = 1 -- | | Returns ABI version. | -- +------------------------------+------------------------------------------+ -- | authserver_create | (method: u8) -> c_int (slot) | --- | | Creates session in Active state. | +-- | | Creates a live context, not an authenticated identity. | -- | | Returns -1 on failure. | -- +------------------------------+------------------------------------------+ -- | authserver_destroy | (slot: c_int) -> void | @@ -63,19 +57,21 @@ abiVersion = 1 -- +------------------------------+------------------------------------------+ -- | authserver_authenticate | (slot: c_int, method: u8) | -- | | -> u8 (AuthResult tag) | --- | | Attempt authentication with method. | +-- | | Never succeeds: InvalidCredentials until | +-- | | lockout, then AccountLocked; no credentials. | -- +------------------------------+------------------------------------------+ -- | authserver_require_mfa | (slot: c_int, mfa_method: u8) | -- | | -> u8 (0=ok, 1=rejected) | --- | | Set MFA requirement on session. | +-- | | Set a modeled MFA policy only. | -- +------------------------------+------------------------------------------+ -- | authserver_verify_mfa | (slot: c_int, mfa_method: u8) | -- | | -> u8 (0=ok, 1=rejected) | --- | | Verify MFA challenge response. | +-- | | Always rejects: no challenge material. | -- +------------------------------+------------------------------------------+ -- | authserver_issue_token | (slot: c_int, token_type: u8) | -- | | -> u8 (0=ok, 1=rejected) | --- | | Issue a token of the given type. | +-- | | Always rejects without authenticated | +-- | | state; this model has no authenticator. | -- +------------------------------+------------------------------------------+ -- | authserver_token_count | (slot: c_int) -> u32 | -- | | Returns number of active tokens. | diff --git a/protocols/proven-authserver/src/Main.idr b/protocols/proven-authserver/src/Main.idr index 37ccaa46..06fde473 100644 --- a/protocols/proven-authserver/src/Main.idr +++ b/protocols/proven-authserver/src/Main.idr @@ -20,7 +20,7 @@ import Authserver covering main : IO () main = do - putStrLn "proven-authserver v0.1.0 -- authentication server" + putStrLn "proven-authserver v0.1.0 -- authentication lifecycle model (no credential verifier)" putStrLn "" putStrLn $ "Auth port: " ++ show authPort putStrLn $ "Token TTL: " ++ show tokenTTL ++ " seconds" diff --git a/protocols/proven-backup/ffi/zig/src/backup.zig b/protocols/proven-backup/ffi/zig/src/backup.zig index 3449c6ef..06685bc2 100644 --- a/protocols/proven-backup/ffi/zig/src/backup.zig +++ b/protocols/proven-backup/ffi/zig/src/backup.zig @@ -259,8 +259,9 @@ pub export fn backup_start(slot: c_int) callconv(.c) u8 { return 0; } -/// Begin verification. Returns 0 on success, 1 on rejection. -/// Transitions: Running -> Verifying. +/// Verify backup contents. There is no backup artifact/verifier in this +/// state-machine-only implementation, so verification always rejects and a +/// running job is marked Failed rather than advancing to a false success. pub export fn backup_verify(slot: c_int) callconv(.c) u8 { mutex.lock(); defer mutex.unlock(); @@ -268,8 +269,8 @@ pub export fn backup_verify(slot: c_int) callconv(.c) u8 { const idx = validSlot(slot) orelse return 1; if (jobs[idx].state != .running) return 1; - jobs[idx].state = .verifying; - return 0; + jobs[idx].state = .failed; + return 1; } /// Complete the backup. Returns 0 on success, 1 on rejection. diff --git a/protocols/proven-backup/ffi/zig/test/integration_test.zig b/protocols/proven-backup/ffi/zig/test/integration_test.zig index 130b0c4b..ae629403 100644 --- a/protocols/proven-backup/ffi/zig/test/integration_test.zig +++ b/protocols/proven-backup/ffi/zig/test/integration_test.zig @@ -125,23 +125,23 @@ test "start transitions Idle -> Running" { try std.testing.expectEqual(@as(u8, 1), backup.backup_state(slot)); // Running } -test "verify transitions Running -> Verifying" { +test "verification fails closed and marks a running job Failed" { const slot = backup.backup_create(0, 4, 0, 0); defer backup.backup_destroy(slot); _ = backup.backup_start(slot); - try std.testing.expectEqual(@as(u8, 0), backup.backup_verify(slot)); - try std.testing.expectEqual(@as(u8, 2), backup.backup_state(slot)); // Verifying + try std.testing.expectEqual(@as(u8, 1), backup.backup_verify(slot)); // no artifact verifier + try std.testing.expectEqual(@as(u8, 4), backup.backup_state(slot)); // Failed } -test "complete transitions Verifying -> Complete" { +test "a backup cannot complete after unavailable verification" { const slot = backup.backup_create(0, 4, 0, 0); defer backup.backup_destroy(slot); _ = backup.backup_start(slot); - _ = backup.backup_verify(slot); - try std.testing.expectEqual(@as(u8, 0), backup.backup_complete(slot)); - try std.testing.expectEqual(@as(u8, 3), backup.backup_state(slot)); // Complete + try std.testing.expectEqual(@as(u8, 1), backup.backup_verify(slot)); + try std.testing.expectEqual(@as(u8, 1), backup.backup_complete(slot)); + try std.testing.expectEqual(@as(u8, 4), backup.backup_state(slot)); // Failed, never Complete } test "fail transitions Running -> Failed" { @@ -153,13 +153,13 @@ test "fail transitions Running -> Failed" { try std.testing.expectEqual(@as(u8, 4), backup.backup_state(slot)); // Failed } -test "fail transitions Verifying -> Failed" { +test "unavailable verification cannot be reported as another successful transition" { const slot = backup.backup_create(0, 4, 0, 0); defer backup.backup_destroy(slot); _ = backup.backup_start(slot); - _ = backup.backup_verify(slot); - try std.testing.expectEqual(@as(u8, 0), backup.backup_fail(slot)); + try std.testing.expectEqual(@as(u8, 1), backup.backup_verify(slot)); + try std.testing.expectEqual(@as(u8, 1), backup.backup_fail(slot)); // already Failed try std.testing.expectEqual(@as(u8, 4), backup.backup_state(slot)); // Failed } @@ -195,13 +195,12 @@ test "set_retention rejects invalid policy" { // Reset // ========================================================================= -test "reset Complete -> Idle" { +test "reset after verification failure clears only the Failed job state" { const slot = backup.backup_create(0, 4, 0, 0); defer backup.backup_destroy(slot); _ = backup.backup_start(slot); - _ = backup.backup_verify(slot); - _ = backup.backup_complete(slot); + try std.testing.expectEqual(@as(u8, 1), backup.backup_verify(slot)); try std.testing.expectEqual(@as(u8, 0), backup.backup_reset(slot)); try std.testing.expectEqual(@as(u8, 0), backup.backup_state(slot)); // Idle } @@ -286,12 +285,12 @@ test "cannot complete from Running" { try std.testing.expectEqual(@as(u8, 1), backup.backup_complete(slot)); } -test "cannot cancel from Verifying" { +test "cannot cancel after verification fails closed" { const slot = backup.backup_create(0, 4, 0, 0); defer backup.backup_destroy(slot); _ = backup.backup_start(slot); - _ = backup.backup_verify(slot); + try std.testing.expectEqual(@as(u8, 1), backup.backup_verify(slot)); try std.testing.expectEqual(@as(u8, 1), backup.backup_cancel(slot)); } diff --git a/protocols/proven-backup/proven-backup.ipkg b/protocols/proven-backup/proven-backup.ipkg index f9e5579e..4297421d 100644 --- a/protocols/proven-backup/proven-backup.ipkg +++ b/protocols/proven-backup/proven-backup.ipkg @@ -5,7 +5,7 @@ package proven-backup version = "0.1.0" authors = "Jonathan D.A. Jewell" license = "MPL-2.0" -brief = "Proven backup server skeleton" +brief = "Backup job state-machine skeleton; no data backend, verification fails closed" sourcedir = "src" main = Main diff --git a/protocols/proven-backup/src/BackupABI/Foreign.idr b/protocols/proven-backup/src/BackupABI/Foreign.idr index d83da6e2..c832c421 100644 --- a/protocols/proven-backup/src/BackupABI/Foreign.idr +++ b/protocols/proven-backup/src/BackupABI/Foreign.idr @@ -6,12 +6,9 @@ -- Declares the opaque handle type and documents the complete FFI contract -- that the Zig implementation (ffi/zig/src/backup.zig) must provide. -- --- The Zig FFI manages: --- - 64-slot mutex-protected backup job pool --- - Backup job configuration (type, schedule, compression, encryption) --- - Job lifecycle state machine --- - Retention policy enforcement --- - Verification tracking +-- The Zig FFI manages in-memory job metadata and lifecycle state only. It has +-- no backup-data backend; backup_verify rejects and moves Running jobs to +-- Failed rather than claiming content was verified. -- -- All functions use C calling convention and communicate state via -- Bits8 tags matching BackupABI.Types exactly. @@ -66,10 +63,11 @@ abiVersion = 1 -- | | Transitions Idle -> Running. | -- +-----------------------------+-------------------------------------------+ -- | backup_verify | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | Transitions Running -> Verifying. | +-- | | Fails closed without a data verifier; | +-- | | transitions Running -> Failed and returns 1.| -- +-----------------------------+-------------------------------------------+ -- | backup_complete | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | Transitions Verifying -> Complete. | +-- | | Requires Verifying; unreachable until a real verifier exists. | -- +-----------------------------+-------------------------------------------+ -- | backup_fail | (slot: c_int) -> u8 (0=ok, 1=rejected) | -- | | Transitions Running/Verifying -> Failed. | diff --git a/protocols/proven-backup/src/Main.idr b/protocols/proven-backup/src/Main.idr index ccceab78..04004fbb 100644 --- a/protocols/proven-backup/src/Main.idr +++ b/protocols/proven-backup/src/Main.idr @@ -9,7 +9,6 @@ import Backup %default total ||| Print server name, port, and enumerate all type constructors. -partial main : IO () main = do putStrLn "==========================================" diff --git a/protocols/proven-ca/ffi/zig/src/ca.zig b/protocols/proven-ca/ffi/zig/src/ca.zig index 8b8c7a3f..d094c250 100644 --- a/protocols/proven-ca/ffi/zig/src/ca.zig +++ b/protocols/proven-ca/ffi/zig/src/ca.zig @@ -3,14 +3,12 @@ // // ca.zig -- Zig FFI implementation of proven-ca. // -// Implements verified certificate authority state machine with: -// - Slot-based CA context management (up to 64 concurrent contexts) -// - Per-context certificate store (up to 64 certs per context) -// - Certificate lifecycle enforcement matching Idris2 Transitions.idr -// - CA hierarchy validation matching CanIssue GADT -// - CRL management with status tracking -// - OCSP responder state per context +// Implements a certificate-lifecycle metadata model with: +// - Slot-based CA context management and certificate metadata +// - Hierarchy policy checks matching CanIssue GADT +// - Fail-closed X.509 signing, chain validation, CRL, and OCSP operations // - Thread-safe via mutex +// It does not issue signed X.509 certificates or provide a production CA. const std = @import("std"); @@ -355,14 +353,17 @@ pub export fn ca_issue_cert(slot: c_int, cert_type_tag: u8, key_algo_tag: u8, si // -- Certificate state transitions -------------------------------------------- +/// Reject certificate activation until X.509 encoding and cryptographic signing +/// are implemented. A pending metadata record is never promoted to Active. pub export fn ca_sign_cert(slot: c_int, cert_id: c_int) callconv(.c) u8 { mutex.lock(); defer mutex.unlock(); const ctx_idx = validContext(slot) orelse return 1; const cid = validCert(ctx_idx, cert_id) orelse return 1; if (contexts[ctx_idx].certs[cid].state != .pending) return 1; - contexts[ctx_idx].certs[cid].state = .active; - return 0; + // No issuer private-key material, certificate encoder, or signature + // backend is available, so claiming a successful signature is unsafe. + return 1; } pub export fn ca_revoke_cert(slot: c_int, cert_id: c_int, reason_tag: u8) callconv(.c) u8 { @@ -485,28 +486,15 @@ pub export fn ca_cert_count(slot: c_int) callconv(.c) c_int { // -- Chain validation --------------------------------------------------------- +/// Validate a serialized X.509 chain. This FFI currently stores only +/// certificate metadata and has no DER parser or signature verifier, so it +/// rejects every otherwise-valid record rather than reporting a false proof. pub export fn ca_validate_chain(slot: c_int, cert_id: c_int) callconv(.c) u8 { mutex.lock(); defer mutex.unlock(); const ctx_idx = validContext(slot) orelse return 1; - const cid = validCert(ctx_idx, cert_id) orelse return 1; - const cert = &contexts[ctx_idx].certs[cid]; - - // Self-signed root: valid chain of length 1 - if (cert.cert_type == .root and cert.issuer_id == -1) return 0; - - // Must have an issuer - if (cert.issuer_id < 0) return 1; - const issuer_cid = validCert(ctx_idx, cert.issuer_id) orelse return 1; - const issuer = &contexts[ctx_idx].certs[issuer_cid]; - - // Issuer must be Active - if (issuer.state != .active and issuer.state != .pending) return 1; - - // Check CanIssue relationship - if (!canIssueCheck(@intFromEnum(issuer.cert_type), @intFromEnum(cert.cert_type))) return 1; - - return 0; // chain valid + _ = validCert(ctx_idx, cert_id) orelse return 1; + return 1; // X.509 chain verification backend unavailable. } pub export fn ca_set_issuer(slot: c_int, cert_id: c_int, issuer_id: c_int) callconv(.c) u8 { @@ -552,13 +540,14 @@ pub export fn ca_crl_status(slot: c_int) callconv(.c) u8 { return @intFromEnum(contexts[ctx_idx].crl_status); } +/// Reject CRL refresh until revocation entries can be serialized and the CRL +/// can be signed. Surface the unavailable state instead of claiming success. pub export fn ca_update_crl(slot: c_int) callconv(.c) u8 { mutex.lock(); defer mutex.unlock(); const ctx_idx = validContext(slot) orelse return 1; - // Transition CRL to current state (simulates successful CRL generation) - contexts[ctx_idx].crl_status = .current; - return 0; + contexts[ctx_idx].crl_status = .crl_error; + return 1; } // -- OCSP responder ----------------------------------------------------------- @@ -570,19 +559,18 @@ pub export fn ca_ocsp_status(slot: c_int) callconv(.c) u8 { return @intFromEnum(contexts[ctx_idx].ocsp_status); } +/// Query OCSP status. The responder/backend is not implemented, so every +/// query fails closed as Unavailable and the context never advertises service. pub export fn ca_ocsp_query(slot: c_int, cert_id: c_int) callconv(.c) u8 { mutex.lock(); defer mutex.unlock(); const ctx_idx = validContext(slot) orelse return 3; // unavailable - const cid = validCert(ctx_idx, cert_id) orelse return 2; // unknown - const state = contexts[ctx_idx].certs[cid].state; - // Update OCSP responder status to reflect it is serving - contexts[ctx_idx].ocsp_status = .good; - return switch (state) { - .active => 0, // good - .revoked => 1, // revoked - .pending, .expired, .suspended => 2, // unknown (not definitively good/revoked) + _ = validCert(ctx_idx, cert_id) orelse { + contexts[ctx_idx].ocsp_status = .unavailable; + return 3; }; + contexts[ctx_idx].ocsp_status = .unavailable; + return 3; } // -- Validity period ---------------------------------------------------------- diff --git a/protocols/proven-ca/ffi/zig/test/ca_test.zig b/protocols/proven-ca/ffi/zig/test/ca_test.zig index 822d7e7b..d5e05784 100644 --- a/protocols/proven-ca/ffi/zig/test/ca_test.zig +++ b/protocols/proven-ca/ffi/zig/test/ca_test.zig @@ -139,85 +139,59 @@ test "issue cert rejects invalid sig algo" { } // ========================================================================= -// Certificate lifecycle: full cycle +// Certificate lifecycle fail-closed behavior // ========================================================================= -test "full lifecycle: Pending -> Active -> Suspended -> Active -> Revoked" { +test "pending certificate is never activated without a signing backend" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); - const cert = ca.ca_issue_cert(slot, 2, 4, 5); // EndEntity, Ed25519, PureEd25519 + const cert = ca.ca_issue_cert(slot, 2, 4, 5); // metadata only try std.testing.expect(cert >= 0); - // Sign: Pending -> Active - try std.testing.expectEqual(@as(u8, 0), ca.ca_sign_cert(slot, cert)); - try std.testing.expectEqual(@as(u8, 1), ca.ca_cert_state(slot, cert)); // Active - - // Suspend: Active -> Suspended - try std.testing.expectEqual(@as(u8, 0), ca.ca_suspend_cert(slot, cert)); - try std.testing.expectEqual(@as(u8, 4), ca.ca_cert_state(slot, cert)); // Suspended - - // Reinstate: Suspended -> Active - try std.testing.expectEqual(@as(u8, 0), ca.ca_reinstate_cert(slot, cert)); - try std.testing.expectEqual(@as(u8, 1), ca.ca_cert_state(slot, cert)); // Active - - // Revoke: Active -> Revoked - try std.testing.expectEqual(@as(u8, 0), ca.ca_revoke_cert(slot, cert, 1)); // KeyCompromise - try std.testing.expectEqual(@as(u8, 2), ca.ca_cert_state(slot, cert)); // Revoked + try std.testing.expectEqual(@as(u8, 1), ca.ca_sign_cert(slot, cert)); + try std.testing.expectEqual(@as(u8, 0), ca.ca_cert_state(slot, cert)); // remains Pending + try std.testing.expectEqual(@as(u8, 1), ca.ca_suspend_cert(slot, cert)); + try std.testing.expectEqual(@as(u8, 1), ca.ca_reinstate_cert(slot, cert)); + try std.testing.expectEqual(@as(u8, 1), ca.ca_revoke_cert(slot, cert, 1)); + try std.testing.expectEqual(@as(u8, 1), ca.ca_expire_cert(slot, cert)); + try std.testing.expectEqual(@as(c_int, -1), ca.ca_renew_cert(slot, cert)); + try std.testing.expectEqual(@as(u8, 0), ca.ca_cert_state(slot, cert)); } -test "expire: Active -> Expired" { +test "expire rejects an unsigned pending metadata record" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); - const cert = ca.ca_issue_cert(slot, 0, 0, 0); // Root, RSA2048, SHA256WithRSA - _ = ca.ca_sign_cert(slot, cert); - try std.testing.expectEqual(@as(u8, 0), ca.ca_expire_cert(slot, cert)); - try std.testing.expectEqual(@as(u8, 3), ca.ca_cert_state(slot, cert)); // Expired + const cert = ca.ca_issue_cert(slot, 0, 0, 0); + try std.testing.expectEqual(@as(u8, 1), ca.ca_sign_cert(slot, cert)); + try std.testing.expectEqual(@as(u8, 1), ca.ca_expire_cert(slot, cert)); + try std.testing.expectEqual(@as(u8, 0), ca.ca_cert_state(slot, cert)); // Pending } -test "renew: Active -> new Pending cert" { +test "renew rejects a certificate that was never signed" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); - const cert = ca.ca_issue_cert(slot, 1, 2, 3); // Intermediate, ECDSA_P256, SHA256WithECDSA - _ = ca.ca_sign_cert(slot, cert); - const new_cert = ca.ca_renew_cert(slot, cert); - try std.testing.expect(new_cert >= 0); - try std.testing.expect(new_cert != cert); - try std.testing.expectEqual(@as(u8, 0), ca.ca_cert_state(slot, new_cert)); // Pending - try std.testing.expectEqual(@as(u8, 1), ca.ca_cert_type(slot, new_cert)); // Same type + const cert = ca.ca_issue_cert(slot, 1, 2, 3); + try std.testing.expectEqual(@as(u8, 1), ca.ca_sign_cert(slot, cert)); + try std.testing.expectEqual(@as(c_int, -1), ca.ca_renew_cert(slot, cert)); + try std.testing.expectEqual(@as(c_int, 1), ca.ca_cert_count(slot)); } // ========================================================================= // Invalid transitions (impossibility proofs from Transitions.idr) // ========================================================================= -test "revoked is terminal: cannot sign, suspend, expire, or renew" { +test "unsigned pending metadata cannot enter terminal lifecycle states" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const cert = ca.ca_issue_cert(slot, 2, 0, 0); - _ = ca.ca_sign_cert(slot, cert); - _ = ca.ca_revoke_cert(slot, cert, 0); - // All transitions from Revoked must fail - try std.testing.expectEqual(@as(u8, 1), ca.ca_sign_cert(slot, cert)); - try std.testing.expectEqual(@as(u8, 1), ca.ca_suspend_cert(slot, cert)); + try std.testing.expectEqual(@as(u8, 1), ca.ca_revoke_cert(slot, cert, 0)); try std.testing.expectEqual(@as(u8, 1), ca.ca_expire_cert(slot, cert)); - try std.testing.expectEqual(@as(u8, 1), ca.ca_reinstate_cert(slot, cert)); - try std.testing.expectEqual(@as(c_int, -1), ca.ca_renew_cert(slot, cert)); -} - -test "expired is terminal: cannot sign, suspend, revoke, or renew" { - const slot = ca.ca_create(); - defer ca.ca_destroy(slot); - const cert = ca.ca_issue_cert(slot, 2, 0, 0); - _ = ca.ca_sign_cert(slot, cert); - _ = ca.ca_expire_cert(slot, cert); - // All transitions from Expired must fail try std.testing.expectEqual(@as(u8, 1), ca.ca_sign_cert(slot, cert)); - try std.testing.expectEqual(@as(u8, 1), ca.ca_suspend_cert(slot, cert)); - try std.testing.expectEqual(@as(u8, 1), ca.ca_revoke_cert(slot, cert, 0)); - try std.testing.expectEqual(@as(u8, 1), ca.ca_reinstate_cert(slot, cert)); - try std.testing.expectEqual(@as(c_int, -1), ca.ca_renew_cert(slot, cert)); + try std.testing.expectEqual(@as(u8, 0), ca.ca_cert_state(slot, cert)); // Pending } + + test "cannot suspend from Pending" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); @@ -232,29 +206,27 @@ test "cannot expire from Pending" { try std.testing.expectEqual(@as(u8, 1), ca.ca_expire_cert(slot, cert)); } -test "cannot reinstate from Active" { +test "cannot reinstate from Pending" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const cert = ca.ca_issue_cert(slot, 2, 0, 0); - _ = ca.ca_sign_cert(slot, cert); + try std.testing.expectEqual(@as(u8, 1), ca.ca_sign_cert(slot, cert)); try std.testing.expectEqual(@as(u8, 1), ca.ca_reinstate_cert(slot, cert)); } -test "revoke from Suspended works" { +test "revoke is unavailable until a certificate is cryptographically active" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const cert = ca.ca_issue_cert(slot, 2, 0, 0); - _ = ca.ca_sign_cert(slot, cert); - _ = ca.ca_suspend_cert(slot, cert); - try std.testing.expectEqual(@as(u8, 0), ca.ca_revoke_cert(slot, cert, 2)); // CACompromise - try std.testing.expectEqual(@as(u8, 2), ca.ca_cert_state(slot, cert)); // Revoked + try std.testing.expectEqual(@as(u8, 1), ca.ca_sign_cert(slot, cert)); + try std.testing.expectEqual(@as(u8, 1), ca.ca_revoke_cert(slot, cert, 2)); + try std.testing.expectEqual(@as(u8, 0), ca.ca_cert_state(slot, cert)); // Pending } test "revoke rejects invalid reason tag" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const cert = ca.ca_issue_cert(slot, 2, 0, 0); - _ = ca.ca_sign_cert(slot, cert); try std.testing.expectEqual(@as(u8, 1), ca.ca_revoke_cert(slot, cert, 99)); } @@ -316,25 +288,14 @@ test "ca_can_issue matches Transitions.idr CanIssue" { // Chain validation // ========================================================================= -test "self-signed root chain is valid" { +test "chain validation rejects metadata without DER and signatures" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); - const root = ca.ca_issue_cert(slot, 0, 1, 1); // Root - _ = ca.ca_sign_cert(slot, root); - // Root with no issuer (-1) is self-signed and valid - try std.testing.expectEqual(@as(u8, 0), ca.ca_validate_chain(slot, root)); -} - -test "intermediate issued by root chain is valid" { - const slot = ca.ca_create(); - defer ca.ca_destroy(slot); - const root = ca.ca_issue_cert(slot, 0, 1, 1); // Root - _ = ca.ca_sign_cert(slot, root); - const inter = ca.ca_issue_cert(slot, 1, 2, 3); // Intermediate - // Set issuer + const root = ca.ca_issue_cert(slot, 0, 1, 1); + const inter = ca.ca_issue_cert(slot, 1, 2, 3); try std.testing.expectEqual(@as(u8, 0), ca.ca_set_issuer(slot, inter, root)); - _ = ca.ca_sign_cert(slot, inter); - try std.testing.expectEqual(@as(u8, 0), ca.ca_validate_chain(slot, inter)); + try std.testing.expectEqual(@as(u8, 1), ca.ca_validate_chain(slot, root)); + try std.testing.expectEqual(@as(u8, 1), ca.ca_validate_chain(slot, inter)); } test "set_issuer rejects invalid hierarchy" { @@ -363,11 +324,11 @@ test "initial CRL status is pending" { try std.testing.expectEqual(@as(u8, 2), ca.ca_crl_status(slot)); // crl_pending } -test "update_crl transitions to current" { +test "update_crl fails closed without CRL generation and signing" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); - try std.testing.expectEqual(@as(u8, 0), ca.ca_update_crl(slot)); - try std.testing.expectEqual(@as(u8, 0), ca.ca_crl_status(slot)); // current + try std.testing.expectEqual(@as(u8, 1), ca.ca_update_crl(slot)); + try std.testing.expectEqual(@as(u8, 3), ca.ca_crl_status(slot)); // crl_error } test "crl_status on invalid slot returns error" { @@ -384,30 +345,26 @@ test "initial OCSP status is unavailable" { try std.testing.expectEqual(@as(u8, 3), ca.ca_ocsp_status(slot)); // unavailable } -test "ocsp_query returns good for active cert" { +test "OCSP is unavailable even for a pending certificate" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const cert = ca.ca_issue_cert(slot, 2, 4, 5); - _ = ca.ca_sign_cert(slot, cert); - try std.testing.expectEqual(@as(u8, 0), ca.ca_ocsp_query(slot, cert)); // good - // OCSP status should now be 'good' (responder is serving) - try std.testing.expectEqual(@as(u8, 0), ca.ca_ocsp_status(slot)); + try std.testing.expectEqual(@as(u8, 3), ca.ca_ocsp_query(slot, cert)); // unavailable + try std.testing.expectEqual(@as(u8, 3), ca.ca_ocsp_status(slot)); } -test "ocsp_query returns revoked for revoked cert" { +test "OCSP query for an invalid certificate is unavailable" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); - const cert = ca.ca_issue_cert(slot, 2, 0, 0); - _ = ca.ca_sign_cert(slot, cert); - _ = ca.ca_revoke_cert(slot, cert, 0); - try std.testing.expectEqual(@as(u8, 1), ca.ca_ocsp_query(slot, cert)); // revoked + try std.testing.expectEqual(@as(u8, 3), ca.ca_ocsp_query(slot, 999)); + try std.testing.expectEqual(@as(u8, 3), ca.ca_ocsp_status(slot)); } -test "ocsp_query returns unknown for pending cert" { +test "OCSP does not report an unsigned pending certificate as good" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const cert = ca.ca_issue_cert(slot, 2, 0, 0); - try std.testing.expectEqual(@as(u8, 2), ca.ca_ocsp_query(slot, cert)); // unknown + try std.testing.expectEqual(@as(u8, 3), ca.ca_ocsp_query(slot, cert)); // unavailable } test "ocsp_query on invalid slot returns unavailable" { @@ -521,15 +478,15 @@ test "next_serial is always greater than last issued serial" { try std.testing.expect(next_after > serial); } -test "renewed cert gets new serial" { +test "renewal does not allocate a new certificate before signing is available" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const cert = ca.ca_issue_cert(slot, 1, 2, 3); - _ = ca.ca_sign_cert(slot, cert); const old_serial = ca.ca_cert_serial(slot, cert); - const new_cert = ca.ca_renew_cert(slot, cert); - const new_serial = ca.ca_cert_serial(slot, new_cert); - try std.testing.expect(new_serial > old_serial); + try std.testing.expectEqual(@as(u8, 1), ca.ca_sign_cert(slot, cert)); + try std.testing.expectEqual(@as(c_int, -1), ca.ca_renew_cert(slot, cert)); + try std.testing.expectEqual(@as(c_int, 1), ca.ca_cert_count(slot)); + try std.testing.expectEqual(old_serial, ca.ca_cert_serial(slot, cert)); } test "serial query safe on invalid slot" { @@ -567,7 +524,6 @@ test "validate_path_length: child < parent is valid" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const root = ca.ca_issue_cert(slot, 0, 0, 0); - _ = ca.ca_sign_cert(slot, root); _ = ca.ca_set_path_length(slot, root, 2); const inter = ca.ca_issue_cert(slot, 1, 0, 0); _ = ca.ca_set_issuer(slot, inter, root); @@ -579,7 +535,6 @@ test "validate_path_length: child >= parent is invalid" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const root = ca.ca_issue_cert(slot, 0, 0, 0); - _ = ca.ca_sign_cert(slot, root); _ = ca.ca_set_path_length(slot, root, 1); const inter = ca.ca_issue_cert(slot, 1, 0, 0); _ = ca.ca_set_issuer(slot, inter, root); @@ -591,7 +546,6 @@ test "validate_path_length: zero blocks further intermediates" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const root = ca.ca_issue_cert(slot, 0, 0, 0); - _ = ca.ca_sign_cert(slot, root); _ = ca.ca_set_path_length(slot, root, 0); const inter = ca.ca_issue_cert(slot, 1, 0, 0); _ = ca.ca_set_issuer(slot, inter, root); @@ -692,27 +646,23 @@ test "key_usage query safe on invalid slot" { } // ========================================================================= -// Revocation irreversibility (FFI enforcement) +// Fail-closed certificate signing and revocation // ========================================================================= -test "revoked cert cannot be re-signed (irreversible)" { +test "certificate signing failure leaves state Pending across retries" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const cert = ca.ca_issue_cert(slot, 2, 0, 0); - _ = ca.ca_sign_cert(slot, cert); - _ = ca.ca_revoke_cert(slot, cert, 1); - // Attempt to sign again must fail try std.testing.expectEqual(@as(u8, 1), ca.ca_sign_cert(slot, cert)); - // State must remain Revoked - try std.testing.expectEqual(@as(u8, 2), ca.ca_cert_state(slot, cert)); + try std.testing.expectEqual(@as(u8, 1), ca.ca_sign_cert(slot, cert)); + try std.testing.expectEqual(@as(u8, 0), ca.ca_cert_state(slot, cert)); // Pending } -test "double revocation is idempotent rejection" { +test "pending metadata cannot be revoked, including on repeated attempts" { const slot = ca.ca_create(); defer ca.ca_destroy(slot); const cert = ca.ca_issue_cert(slot, 2, 0, 0); - _ = ca.ca_sign_cert(slot, cert); - _ = ca.ca_revoke_cert(slot, cert, 0); - // Second revocation attempt fails (already terminal) try std.testing.expectEqual(@as(u8, 1), ca.ca_revoke_cert(slot, cert, 0)); + try std.testing.expectEqual(@as(u8, 1), ca.ca_revoke_cert(slot, cert, 0)); + try std.testing.expectEqual(@as(u8, 0), ca.ca_cert_state(slot, cert)); } diff --git a/protocols/proven-ca/proven-ca.ipkg b/protocols/proven-ca/proven-ca.ipkg index aee71a3f..ee8f9531 100644 --- a/protocols/proven-ca/proven-ca.ipkg +++ b/protocols/proven-ca/proven-ca.ipkg @@ -5,7 +5,7 @@ package proven-ca version = "0.2.0" authors = "Jonathan D.A. Jewell" license = "MPL-2.0" -brief = "Certificate Authority with X.509 lifecycle management and ABI-FFI bridge" +brief = "X.509 lifecycle metadata model; signing, chain, CRL, and OCSP fail closed" sourcedir = "src" main = Main diff --git a/protocols/proven-ca/src/CAABI/Foreign.idr b/protocols/proven-ca/src/CAABI/Foreign.idr index a36c3402..90dea24c 100644 --- a/protocols/proven-ca/src/CAABI/Foreign.idr +++ b/protocols/proven-ca/src/CAABI/Foreign.idr @@ -3,9 +3,9 @@ -- -- CAABI.Foreign: Foreign function declarations for the C bridge. -- --- Declares the opaque handle type and documents the complete FFI contract --- that the Zig implementation must provide. The CA FFI manages certificate --- lifecycle, chain validation, CRL management, and OCSP responder state. +-- Declares the opaque handle type and documents the FFI contract. The Zig +-- implementation stores lifecycle metadata only; signing, chain validation, +-- CRL generation, and OCSP queries fail closed without cryptographic backends. module CAABI.Foreign @@ -49,10 +49,10 @@ abiVersion = 1 -- +-------------------------+-----------------------------------------------+ -- | ca_issue_cert | (slot: c_int, cert_type: u8, key_algo: u8, | -- | | sig_algo: u8) -> c_int (cert_id, -1=fail) | --- | | Issues a new certificate in Pending state. | +-- | | Allocates Pending metadata only; no X.509 certificate is created. | -- +-------------------------+-----------------------------------------------+ -- | ca_sign_cert | (slot: c_int, cert_id: c_int) -> u8 | --- | | Pending -> Active. 0=ok, 1=rejected. | +-- | | Always rejects without signing; certificate remains Pending. | -- +-------------------------+-----------------------------------------------+ -- | ca_revoke_cert | (slot: c_int, cert_id: c_int, | -- | | reason: u8) -> u8 | @@ -86,7 +86,7 @@ abiVersion = 1 -- | | Number of certificates in this CA context. | -- +-------------------------+-----------------------------------------------+ -- | ca_validate_chain | (slot: c_int, cert_id: c_int) -> u8 | --- | | Validates issuer chain. 0=valid, 1=invalid. | +-- | | Always rejects: no DER parser or signature verifier. | -- +-------------------------+-----------------------------------------------+ -- | ca_can_issue | (issuer: u8, child: u8) -> u8 | -- | | Stateless: can issuer type issue child type? | @@ -100,14 +100,15 @@ abiVersion = 1 -- | | Returns CRLStatus tag for this CA context. | -- +-------------------------+-----------------------------------------------+ -- | ca_update_crl | (slot: c_int) -> u8 | --- | | Refreshes the CRL. 0=ok, 1=error. | +-- | | Always fails closed; sets CrLError without | +-- | | CRL serialization/signing support. | -- +-------------------------+-----------------------------------------------+ -- | ca_ocsp_status | (slot: c_int) -> u8 | -- | | Returns OCSPStatus tag for this CA context. | -- +-------------------------+-----------------------------------------------+ -- | ca_ocsp_query | (slot: c_int, cert_id: c_int) -> u8 | --- | | Queries OCSP for a cert. Returns OCSPStatus | --- | | tag (Good/Revoked/Unknown/Unavailable). | +-- | | Always returns Unavailable until an OCSP backend | +-- | | is implemented. | -- +-------------------------+-----------------------------------------------+ -- | ca_set_issuer | (slot: c_int, cert_id: c_int, | -- | | issuer_id: c_int) -> u8 | diff --git a/protocols/proven-ca/src/Main.idr b/protocols/proven-ca/src/Main.idr index 990e5577..a16d15b9 100644 --- a/protocols/proven-ca/src/Main.idr +++ b/protocols/proven-ca/src/Main.idr @@ -53,7 +53,7 @@ allKeyUsageBits = main : IO () main = do - putStrLn "proven-ca : Certificate Authority server" + putStrLn "proven-ca : X.509 lifecycle metadata model (signing and validation unavailable)" putStrLn $ " Max path length: " ++ show maxPathLength putStrLn $ " Default validity: " ++ show defaultValidityDays ++ " days" putStrLn $ " CRL update interval: " ++ show crlUpdateHours ++ " hours" diff --git a/protocols/proven-ctlog/ffi/zig/src/ctlog.zig b/protocols/proven-ctlog/ffi/zig/src/ctlog.zig index c05471ed..11b8f71d 100644 --- a/protocols/proven-ctlog/ffi/zig/src/ctlog.zig +++ b/protocols/proven-ctlog/ffi/zig/src/ctlog.zig @@ -3,14 +3,10 @@ // // ctlog.zig -- Zig FFI implementation of proven-ctlog. // -// Implements the Certificate Transparency Log (RFC 6962) server state -// machine with: -// - 64-slot mutex-protected session pool -// - Entry submission tracking per session -// - Merkle tree size management -// - STH (Signed Tree Head) lifecycle -// - Inclusion/consistency proof verification (simulated) -// - Thread-safe via per-pool mutex +// Implements a Certificate Transparency lifecycle model only, not an RFC 6962 +// log. Entry submission, STH signing, and Merkle proof verification fail closed; +// no entry bytes are persisted and no Merkle hashes or signatures are produced. +// Remaining session/state transitions are in-memory model operations. // // All exported functions use C calling convention (callconv(.c)) and // communicate state via u8 tags matching CTLogABI.Types.idr exactly. @@ -260,38 +256,20 @@ pub export fn ctlog_tree_size(slot: c_int) callconv(.c) u32 { // -- Submission ----------------------------------------------------------- -/// Submit an entry to the CT log. -/// Returns a SubmissionStatus tag. +/// Entry submission is unavailable: the model does not persist entry bytes or +/// build a Merkle tree. Always rejects so callers cannot mistake metadata for a +/// logged certificate. pub export fn ctlog_submit( slot: c_int, entry_type: u8, data_ptr: [*]const u8, data_len: u32, ) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - + _ = slot; + _ = entry_type; _ = data_ptr; _ = data_len; - - const idx = validSlot(slot) orelse return @intFromEnum(SubmissionStatus.rejected); - if (sessions[idx].state != .active) return @intFromEnum(SubmissionStatus.rejected); - if (entry_type > 1) return @intFromEnum(SubmissionStatus.rejected); - if (sessions[idx].entry_count >= sessions[idx].max_entries) { - return @intFromEnum(SubmissionStatus.rate_limited); - } - - // Find a free entry slot - for (&sessions[idx].entries) |*e| { - if (!e.active) { - e.entry_type = @enumFromInt(entry_type); - e.active = true; - e.merged = false; - sessions[idx].entry_count += 1; - return @intFromEnum(SubmissionStatus.accepted); - } - } - return @intFromEnum(SubmissionStatus.rate_limited); + return @intFromEnum(SubmissionStatus.rejected); } // -- Merge / Sign lifecycle ----------------------------------------------- @@ -329,53 +307,34 @@ pub export fn ctlog_finish_merge(slot: c_int) callconv(.c) u8 { return 0; } -/// Sign a new STH (Signed Tree Head). -/// Transitions Signing -> Active. +/// STH signing is unavailable because no signing key or cryptographic backend +/// is configured. Always rejects and leaves the session state unchanged. pub export fn ctlog_sign_sth(slot: c_int) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - - const idx = validSlot(slot) orelse return 1; - if (sessions[idx].state != .signing) return 1; - sessions[idx].state = .active; - return 0; + _ = slot; + return 1; } // -- Verification --------------------------------------------------------- -/// Verify an inclusion proof for an entry at a given index. -/// Returns a VerificationResult tag. +/// Inclusion verification is unavailable: this model has no Merkle hashes or +/// proof bytes. It never reports a proof as valid. pub export fn ctlog_verify_inclusion(slot: c_int, index: u32) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - - const idx = validSlot(slot) orelse return @intFromEnum(VerificationResult.invalid_proof); - if (index >= sessions[idx].tree_size) { - return @intFromEnum(VerificationResult.invalid_proof); - } - // Simulated: if the entry exists and is merged, proof is valid - if (index < MAX_ENTRIES and sessions[idx].entries[index].active and - sessions[idx].entries[index].merged) - { - return @intFromEnum(VerificationResult.valid_proof); - } + _ = slot; + _ = index; return @intFromEnum(VerificationResult.invalid_proof); } -/// Verify a consistency proof between two tree sizes. -/// Returns a VerificationResult tag. +/// Consistency verification is unavailable: this model has no Merkle hashes, +/// signed tree heads, or proof bytes. It never reports a proof as valid. pub export fn ctlog_verify_consistency( slot: c_int, old_size: u32, new_size: u32, ) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - - const idx = validSlot(slot) orelse return @intFromEnum(VerificationResult.invalid_proof); - if (old_size > new_size) return @intFromEnum(VerificationResult.inconsistent_tree); - if (new_size > sessions[idx].tree_size) return @intFromEnum(VerificationResult.stale_sth); - return @intFromEnum(VerificationResult.valid_proof); + _ = slot; + _ = old_size; + _ = new_size; + return @intFromEnum(VerificationResult.invalid_proof); } // -- Shutdown / Cleanup --------------------------------------------------- diff --git a/protocols/proven-ctlog/ffi/zig/test/integration_test.zig b/protocols/proven-ctlog/ffi/zig/test/integration_test.zig index ee181d33..64feeef5 100644 --- a/protocols/proven-ctlog/ffi/zig/test/integration_test.zig +++ b/protocols/proven-ctlog/ffi/zig/test/integration_test.zig @@ -96,23 +96,24 @@ test "destroy is safe with invalid slot" { // Entry submission // ========================================================================= -test "submit accepts valid X.509 entry" { +test "submit fails closed without persistent log and Merkle-tree implementation" { const name = "submit-log"; const slot = ctlog.ctlog_create(name.ptr, name.len, 1024); defer ctlog.ctlog_destroy(slot); - const cert = "fake-cert-data"; - try std.testing.expectEqual(@as(u8, 0), ctlog.ctlog_submit(slot, 0, cert.ptr, cert.len)); // accepted - try std.testing.expectEqual(@as(u32, 1), ctlog.ctlog_entry_count(slot)); + const cert = "certificate-bytes"; + try std.testing.expectEqual(@as(u8, 3), ctlog.ctlog_submit(slot, 0, cert.ptr, cert.len)); // rejected + try std.testing.expectEqual(@as(u32, 0), ctlog.ctlog_entry_count(slot)); + try std.testing.expectEqual(@as(u32, 0), ctlog.ctlog_tree_size(slot)); } -test "submit accepts precert entry" { +test "precertificate submission also fails closed" { const name = "precert-log"; const slot = ctlog.ctlog_create(name.ptr, name.len, 1024); defer ctlog.ctlog_destroy(slot); - const cert = "fake-precert"; - try std.testing.expectEqual(@as(u8, 0), ctlog.ctlog_submit(slot, 1, cert.ptr, cert.len)); // accepted + const cert = "precertificate-bytes"; + try std.testing.expectEqual(@as(u8, 3), ctlog.ctlog_submit(slot, 1, cert.ptr, cert.len)); } test "submit rejects invalid entry type" { @@ -147,32 +148,30 @@ test "begin_merge transitions Active -> Merging" { try std.testing.expectEqual(@as(u8, 2), ctlog.ctlog_state(slot)); // Merging } -test "finish_merge integrates entries and transitions to Signing" { +test "finish_merge only exercises the lifecycle model, not a Merkle tree" { const name = "finishmerge-log"; const slot = ctlog.ctlog_create(name.ptr, name.len, 1024); defer ctlog.ctlog_destroy(slot); - // Submit two entries - const cert = "cert-data"; - _ = ctlog.ctlog_submit(slot, 0, cert.ptr, cert.len); - _ = ctlog.ctlog_submit(slot, 1, cert.ptr, cert.len); - try std.testing.expectEqual(@as(u32, 0), ctlog.ctlog_tree_size(slot)); // Not merged yet + const cert = "certificate-bytes"; + _ = ctlog.ctlog_submit(slot, 0, cert.ptr, cert.len); // fails closed + try std.testing.expectEqual(@as(u32, 0), ctlog.ctlog_entry_count(slot)); _ = ctlog.ctlog_begin_merge(slot); try std.testing.expectEqual(@as(u8, 0), ctlog.ctlog_finish_merge(slot)); - try std.testing.expectEqual(@as(u32, 2), ctlog.ctlog_tree_size(slot)); // Merged - try std.testing.expectEqual(@as(u8, 3), ctlog.ctlog_state(slot)); // Signing + try std.testing.expectEqual(@as(u32, 0), ctlog.ctlog_tree_size(slot)); + try std.testing.expectEqual(@as(u8, 3), ctlog.ctlog_state(slot)); // Signing model state } -test "sign_sth transitions Signing -> Active" { +test "sign_sth fails closed without a signing backend" { const name = "sign-log"; const slot = ctlog.ctlog_create(name.ptr, name.len, 1024); defer ctlog.ctlog_destroy(slot); _ = ctlog.ctlog_begin_merge(slot); _ = ctlog.ctlog_finish_merge(slot); - try std.testing.expectEqual(@as(u8, 0), ctlog.ctlog_sign_sth(slot)); - try std.testing.expectEqual(@as(u8, 1), ctlog.ctlog_state(slot)); // Active + try std.testing.expectEqual(@as(u8, 1), ctlog.ctlog_sign_sth(slot)); + try std.testing.expectEqual(@as(u8, 3), ctlog.ctlog_state(slot)); // remains Signing } test "begin_merge rejects from non-Active state" { @@ -196,7 +195,7 @@ test "sign_sth rejects from non-Signing state" { // Verification // ========================================================================= -test "verify_inclusion succeeds for merged entry" { +test "verify_inclusion never accepts without Merkle proof material" { const name = "inclusion-log"; const slot = ctlog.ctlog_create(name.ptr, name.len, 1024); defer ctlog.ctlog_destroy(slot); @@ -205,9 +204,8 @@ test "verify_inclusion succeeds for merged entry" { _ = ctlog.ctlog_submit(slot, 0, cert.ptr, cert.len); _ = ctlog.ctlog_begin_merge(slot); _ = ctlog.ctlog_finish_merge(slot); - _ = ctlog.ctlog_sign_sth(slot); - try std.testing.expectEqual(@as(u8, 0), ctlog.ctlog_verify_inclusion(slot, 0)); // valid_proof + try std.testing.expectEqual(@as(u8, 1), ctlog.ctlog_verify_inclusion(slot, 0)); // invalid/unavailable } test "verify_inclusion fails for out-of-range index" { @@ -218,7 +216,7 @@ test "verify_inclusion fails for out-of-range index" { try std.testing.expectEqual(@as(u8, 1), ctlog.ctlog_verify_inclusion(slot, 999)); // invalid_proof } -test "verify_consistency succeeds for valid range" { +test "verify_consistency never accepts without Merkle proof material" { const name = "consistency-log"; const slot = ctlog.ctlog_create(name.ptr, name.len, 1024); defer ctlog.ctlog_destroy(slot); @@ -228,31 +226,24 @@ test "verify_consistency succeeds for valid range" { _ = ctlog.ctlog_submit(slot, 0, cert.ptr, cert.len); _ = ctlog.ctlog_begin_merge(slot); _ = ctlog.ctlog_finish_merge(slot); - _ = ctlog.ctlog_sign_sth(slot); - try std.testing.expectEqual(@as(u8, 0), ctlog.ctlog_verify_consistency(slot, 0, 2)); // valid + try std.testing.expectEqual(@as(u8, 1), ctlog.ctlog_verify_consistency(slot, 0, 2)); // invalid/unavailable } -test "verify_consistency detects inconsistent tree (old > new)" { +test "verify_consistency does not claim a result without proof material" { const name = "inconsistent-log"; const slot = ctlog.ctlog_create(name.ptr, name.len, 1024); defer ctlog.ctlog_destroy(slot); - const cert = "cert"; - _ = ctlog.ctlog_submit(slot, 0, cert.ptr, cert.len); - _ = ctlog.ctlog_begin_merge(slot); - _ = ctlog.ctlog_finish_merge(slot); - _ = ctlog.ctlog_sign_sth(slot); - - try std.testing.expectEqual(@as(u8, 2), ctlog.ctlog_verify_consistency(slot, 5, 1)); // inconsistent + try std.testing.expectEqual(@as(u8, 1), ctlog.ctlog_verify_consistency(slot, 5, 1)); } -test "verify_consistency detects stale STH" { +test "verify_consistency rejects an unsupported stale-tree check" { const name = "stale-log"; const slot = ctlog.ctlog_create(name.ptr, name.len, 1024); defer ctlog.ctlog_destroy(slot); - try std.testing.expectEqual(@as(u8, 3), ctlog.ctlog_verify_consistency(slot, 0, 100)); // stale + try std.testing.expectEqual(@as(u8, 1), ctlog.ctlog_verify_consistency(slot, 0, 100)); } // ========================================================================= diff --git a/protocols/proven-ctlog/proven-ctlog.ipkg b/protocols/proven-ctlog/proven-ctlog.ipkg index cc76b8d1..60781db9 100644 --- a/protocols/proven-ctlog/proven-ctlog.ipkg +++ b/protocols/proven-ctlog/proven-ctlog.ipkg @@ -5,7 +5,7 @@ package proven-ctlog version = "0.1.0" authors = "Jonathan D.A. Jewell" license = "MPL-2.0" -brief = "Proven skeleton for Certificate Transparency Log (RFC 6962)" +brief = "Certificate Transparency lifecycle model; no persistent log, Merkle tree, or signing backend" sourcedir = "src" main = Main diff --git a/protocols/proven-ctlog/src/CTLogABI/Foreign.idr b/protocols/proven-ctlog/src/CTLogABI/Foreign.idr index 941d1d2c..12fc4863 100644 --- a/protocols/proven-ctlog/src/CTLogABI/Foreign.idr +++ b/protocols/proven-ctlog/src/CTLogABI/Foreign.idr @@ -65,30 +65,31 @@ abiVersion = 1 -- | ctlog_submit | (slot: c_int, entry_type: u8, | -- | | data_ptr: ptr, data_len: u32) | -- | | -> u8 (SubmissionStatus tag) | --- | | Submit an entry for inclusion. | +-- | | Always rejects: no persistent log/tree. | -- +-------------------------------+-----------------------------------------+ -- | ctlog_entry_count | (slot: c_int) -> u32 | --- | | Returns total submitted entries. | +-- | | Model counter; submissions fail closed. | -- +-------------------------------+-----------------------------------------+ -- | ctlog_tree_size | (slot: c_int) -> u32 | --- | | Returns current Merkle tree size. | +-- | | Model counter; no Merkle tree exists. | -- +-------------------------------+-----------------------------------------+ -- | ctlog_begin_merge | (slot: c_int) -> u8 (0=ok, 1=rejected) | -- | | Transitions Active -> Merging. | -- +-------------------------------+-----------------------------------------+ -- | ctlog_finish_merge | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | Transitions Merging -> Active or | --- | | Merging -> Signing. | +-- | | In-memory lifecycle model only; | +-- | | does not build a Merkle tree. | -- +-------------------------------+-----------------------------------------+ --- | ctlog_sign_sth | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | Transitions Signing -> Active. | +-- | ctlog_sign_sth | (slot: c_int) -> u8 | +-- | | Always rejects: no signing backend. | -- +-------------------------------+-----------------------------------------+ -- | ctlog_verify_inclusion | (slot: c_int, index: u32) | -- | | -> u8 (VerificationResult tag) | +-- | | Always rejects: no Merkle proof input. | -- +-------------------------------+-----------------------------------------+ -- | ctlog_verify_consistency | (slot: c_int, old_size: u32, | -- | | new_size: u32) | --- | | -> u8 (VerificationResult tag) | +-- | | Always rejects: no Merkle proof input. | -- +-------------------------------+-----------------------------------------+ -- | ctlog_shutdown | (slot: c_int) -> u8 (0=ok, 1=rejected) | -- | | Transitions to Shutdown. | diff --git a/protocols/proven-ctlog/src/Main.idr b/protocols/proven-ctlog/src/Main.idr index 594259da..e12bbf59 100644 --- a/protocols/proven-ctlog/src/Main.idr +++ b/protocols/proven-ctlog/src/Main.idr @@ -11,7 +11,7 @@ import CTLog covering main : IO () main = do - putStrLn "proven-ctlog — Certificate Transparency Log (RFC 6962) skeleton" + putStrLn "proven-ctlog — Certificate Transparency lifecycle model (no RFC 6962 log backend)" putStrLn $ " Port: " ++ show ctlogPort putStrLn $ " Max Chain Length: " ++ show maxChainLength putStrLn $ " Max Merge Delay: " ++ show maxMergeDelay ++ "s" diff --git a/protocols/proven-dns/ffi/zig/src/dns.zig b/protocols/proven-dns/ffi/zig/src/dns.zig index e4edc970..40f89c9a 100644 --- a/protocols/proven-dns/ffi/zig/src/dns.zig +++ b/protocols/proven-dns/ffi/zig/src/dns.zig @@ -3,13 +3,13 @@ // // dns.zig -- Zig FFI implementation of proven-dns. // -// Implements the verified DNS query lifecycle state machine with: +// Implements a bounded DNS state-machine/message-builder model with: // - 64-slot mutex-protected context pool -// - State machine enforcement matching Idris2 DNSABI.Transitions.idr -// - DNS message builder (header + question + answer + authority + additional) -// - Record type encoding (ABI tags <-> IANA wire codes) -// - DNSSEC state machine (enable, key load, sign/validate) +// - A deliberately narrow root-question query subset +// - Responses capped at 512 bytes and non-authoritative/non-recursive flags +// - DNSSEC configuration state; key loading/signing/validation fail closed // - Thread-safe via per-slot mutex pool +// This is not a general resolver or a production DNSSEC implementation. const std = @import("std"); @@ -75,7 +75,7 @@ pub const DnsState = enum(u8) { sent = 4, }; -/// DNSSEC states (matching DNSABI.Transitions.idr). +/// DNSSEC ABI/model tags; key loading, signing, and validation are unavailable. pub const DnssecState = enum(u8) { disabled = 0, enabled = 1, @@ -150,7 +150,7 @@ const Context = struct { state: DnsState, /// Current DNSSEC state. dnssec_state: DnssecState, - /// DNSSEC algorithm (valid when key_loaded or validated). + /// Reserved ABI/model tag; this FFI never selects a cryptographic algorithm. dnssec_algo: u8, /// Response code. rcode: u8, @@ -158,6 +158,8 @@ const Context = struct { query_rtype: u8, /// Parsed query class (ABI tag). query_class: u8, + /// Recursion Desired bit copied from a supported query. + recursion_desired: bool, /// Parsed query transaction ID. transaction_id: u16, /// Whether this slot is in use. @@ -192,6 +194,7 @@ const empty_context: Context = .{ .rcode = 0, .query_rtype = 255, .query_class = 255, + .recursion_desired = false, .transaction_id = 0, .active = false, .answers = [_]ResourceRecord{empty_rr} ** 16, @@ -307,45 +310,38 @@ pub export fn dns_query_class(slot: c_int) callconv(.c) u8 { // -- Lifecycle transitions ---------------------------------------------------- -/// Parse a DNS query from a raw buffer. -/// Transitions: Idle -> QueryReceived. -/// The buffer must contain at least a 12-byte DNS header. +/// Parse only an exact 17-byte standard request (QR=0, OPCODE=QUERY) with +/// one root-name question, a recognized type/class, no other sections, and RD +/// as its only set flag. +/// Other packets are rejected because this context does not retain arbitrary +/// QNAMEs or EDNS data. Transitions: Idle -> QueryReceived on valid input. pub export fn dns_parse_query(slot: c_int, buf: ?[*]const u8, len: u16) callconv(.c) u8 { const idx = validSlot(slot) orelse return 1; mutexes[idx].lock(); defer mutexes[idx].unlock(); if (contexts[idx].state != .idle) return 1; - - // Minimum DNS header is 12 bytes - if (len < 12) return 1; + if (len != 17) return 1; const data = buf orelse return 1; - // Parse transaction ID (bytes 0-1, big-endian) - contexts[idx].transaction_id = (@as(u16, data[0]) << 8) | @as(u16, data[1]); - - // Parse opcode from flags byte (byte 2, bits 1-4) - // We store but don't validate opcode here — the ABI tag check is in Layout.idr - - // If we have a question section (bytes 4-5 > 0), try to extract qtype and qclass - const qdcount: u16 = (@as(u16, data[4]) << 8) | @as(u16, data[5]); - if (qdcount > 0 and len > 12) { - // Skip the QNAME (series of length-prefixed labels ending with 0) - var offset: usize = 12; - while (offset < len) { - const label_len = data[offset]; - offset += 1; - if (label_len == 0) break; - offset += label_len; - } - // Read QTYPE (2 bytes) and QCLASS (2 bytes) if available - if (offset + 4 <= len) { - const wire_type: u16 = (@as(u16, data[offset]) << 8) | @as(u16, data[offset + 1]); - const wire_class: u16 = (@as(u16, data[offset + 2]) << 8) | @as(u16, data[offset + 3]); - contexts[idx].query_rtype = wireTypeToAbiTag(wire_type); - contexts[idx].query_class = wireClassToAbiTag(wire_class); - } + // Accept only a standard query (QR=0, opcode=QUERY), optionally RD=1. + if ((data[2] & 0xFE) != 0 or data[3] != 0) return 1; + if (data[4] != 0 or data[5] != 1) return 1; // exactly one question + for (data[6..12]) |section_count| { + if (section_count != 0) return 1; // unsupported response/additional sections } + if (data[12] != 0) return 1; // only the root QNAME is retained/encoded + const wire_type: u16 = (@as(u16, data[13]) << 8) | @as(u16, data[14]); + const wire_class: u16 = (@as(u16, data[15]) << 8) | @as(u16, data[16]); + const query_type = wireTypeToAbiTag(wire_type); + const query_class = wireClassToAbiTag(wire_class); + if (query_type == 255 or query_class == 255) return 1; + + // Commit parsed data only after the entire minimal packet is validated. + contexts[idx].transaction_id = (@as(u16, data[0]) << 8) | @as(u16, data[1]); + contexts[idx].recursion_desired = (data[2] & 1) != 0; + contexts[idx].query_rtype = query_type; + contexts[idx].query_class = query_class; contexts[idx].state = .query_received; return 0; } @@ -399,6 +395,7 @@ fn addRecord(slot: c_int, section: Section, rtype: u8, rclass: u8, ttl: u32, rda if (rtype > 14) return 1; // invalid ABI record type tag if (rclass > 3) return 1; // invalid ABI query class tag if (rdlen > 256) return 1; // rdata too large + if (rdlen > 0 and rdata == null) return 1; // non-empty rdata requires a pointer var rr: ResourceRecord = empty_rr; rr.rtype = rtype; @@ -445,7 +442,9 @@ pub export fn dns_set_rcode(slot: c_int, rcode_tag: u8) callconv(.c) u8 { /// Build a DNS response message into the provided buffer. /// Transitions: ResponseBuilding -> Sent. -/// The output buffer must be at least 512 bytes. +/// The output buffer must be at least 512 bytes. Responses exceeding that +/// limit are rejected before writing any bytes. DNSSEC-enabled contexts are +/// rejected because no signer is available; unsigned operation remains usable. /// On success, out_len is set to the actual message length. pub export fn dns_build_response(slot: c_int, out: ?[*]u8, out_len: ?*u16) callconv(.c) u8 { const idx = validSlot(slot) orelse return 1; @@ -455,6 +454,12 @@ pub export fn dns_build_response(slot: c_int, out: ?[*]u8, out_len: ?*u16) callc const buf = out orelse return 1; const len_ptr = out_len orelse return 1; + len_ptr.* = 0; + + // DNSSEC was requested, but this implementation cannot produce or verify + // signatures. Never send an unsigned response under an enabled DNSSEC state. + if (contexts[idx].dnssec_state != .disabled) return 1; + if (responseLength(&contexts[idx]) > 512) return 1; var offset: usize = 0; @@ -462,9 +467,10 @@ pub export fn dns_build_response(slot: c_int, out: ?[*]u8, out_len: ?*u16) callc // Transaction ID buf[0] = @truncate(contexts[idx].transaction_id >> 8); buf[1] = @truncate(contexts[idx].transaction_id); - // Flags: QR=1, Opcode=0, AA=1, TC=0, RD=1, RA=1, Z=0, RCODE - buf[2] = 0x85; // 1_0000_1_0_1 = QR=1, Opcode=0, AA=1, TC=0, RD=1 - buf[3] = 0x80 | (contexts[idx].rcode & 0x0F); // RA=1, Z=0, RCODE + // QR=1, standard opcode, AA=0, TC=0, echo RD; RA=0 because recursion + // is not implemented. Do not claim authority or recursive service. + buf[2] = 0x80 | @as(u8, if (contexts[idx].recursion_desired) 1 else 0); + buf[3] = contexts[idx].rcode & 0x0F; // RA=0, Z=0, RCODE // QDCOUNT = 1 (echo back the question) buf[4] = 0; buf[5] = 1; @@ -503,6 +509,24 @@ pub export fn dns_build_response(slot: c_int, out: ?[*]u8, out_len: ?*u16) callc return 0; } +/// Calculate the uncompressed wire length before writing to the caller's +/// fixed-minimum (512-byte) output buffer. +fn responseLength(ctx: *const Context) usize { + return 17 + sectionWireLength(&ctx.answers, ctx.answer_count) + + sectionWireLength(&ctx.authorities, ctx.authority_count) + + sectionWireLength(&ctx.additionals, ctx.additional_count); +} + +fn sectionWireLength(records: *const [16]ResourceRecord, count: u16) usize { + var length: usize = 0; + var i: usize = 0; + while (i < count) : (i += 1) { + // Root owner name (1), TYPE (2), CLASS (2), TTL (4), RDLENGTH (2), RDATA. + length += 11 + @as(usize, records[i].rdlen); + } + return length; +} + /// Write a section of resource records into the output buffer. fn writeSection(buf: [*]u8, start_offset: usize, records: *const [16]ResourceRecord, count: u16) usize { var offset = start_offset; @@ -541,7 +565,7 @@ fn writeSection(buf: [*]u8, start_offset: usize, records: *const [16]ResourceRec // -- DNSSEC operations -------------------------------------------------------- -/// Enable DNSSEC on a context. +/// Mark DNSSEC mode requested. No crypto backend exists; response building fails closed. /// Transitions: DnssecDisabled -> DnssecEnabled. pub export fn dns_enable_dnssec(slot: c_int) callconv(.c) u8 { const idx = validSlot(slot) orelse return 1; @@ -552,38 +576,33 @@ pub export fn dns_enable_dnssec(slot: c_int) callconv(.c) u8 { return 0; } -/// Load a DNSSEC signing key. -/// Transitions: DnssecEnabled -> DnssecKeyLoaded. +/// Load a DNSSEC signing key. This ABI accepts only an algorithm tag, not +/// private-key material, so it cannot load a key and always rejects. pub export fn dns_load_dnssec_key(slot: c_int, algo: u8) callconv(.c) u8 { const idx = validSlot(slot) orelse return 1; mutexes[idx].lock(); defer mutexes[idx].unlock(); - if (contexts[idx].dnssec_state != .enabled) return 1; - if (algo > 4) return 1; // invalid DNSSEC algorithm tag - contexts[idx].dnssec_algo = algo; - contexts[idx].dnssec_state = .key_loaded; - return 0; + if (contexts[idx].dnssec_state != .enabled or algo > 4) return 1; + return 1; // No private key bytes or key-management backend are provided. } -/// Sign the response (DNSSEC). -/// Transitions: DnssecKeyLoaded -> DnssecValidated. -/// Only valid when lifecycle state is ResponseBuilding. +/// Sign the response (DNSSEC). No signing backend is present, so a key tag or +/// state transition is never treated as evidence of a generated signature. pub export fn dns_sign_response(slot: c_int) callconv(.c) u8 { const idx = validSlot(slot) orelse return 1; mutexes[idx].lock(); defer mutexes[idx].unlock(); if (contexts[idx].dnssec_state != .key_loaded) return 1; if (contexts[idx].state != .response_building) return 1; - contexts[idx].dnssec_state = .validated; - return 0; + return 1; } -/// Check DNSSEC validation result. +/// Check DNSSEC validation result. No DNSSEC validator is available. pub export fn dns_validate_dnssec(slot: c_int) callconv(.c) u8 { const idx = validSlot(slot) orelse return 1; mutexes[idx].lock(); defer mutexes[idx].unlock(); - return if (contexts[idx].dnssec_state == .validated) 0 else 1; + return 1; } // -- Stateless transition checks ---------------------------------------------- @@ -602,7 +621,7 @@ pub export fn dns_can_transition(from: u8, to: u8) callconv(.c) u8 { return 0; } -/// Check whether a DNSSEC state transition is valid. +/// Check the abstract DNSSEC state model only; this does not imply that crypto is available. /// Matches DNSABI.Transitions.validateDnssecTransition exactly. pub export fn dns_can_dnssec_transition(from: u8, to: u8) callconv(.c) u8 { if (from == 0 and to == 1) return 1; // Disabled -> Enabled diff --git a/protocols/proven-dns/ffi/zig/test/dns_test.zig b/protocols/proven-dns/ffi/zig/test/dns_test.zig index e8c8785c..316a80ae 100644 --- a/protocols/proven-dns/ffi/zig/test/dns_test.zig +++ b/protocols/proven-dns/ffi/zig/test/dns_test.zig @@ -130,7 +130,7 @@ test "destroy is safe with invalid slot" { } // ========================================================================= -// Full lifecycle — Idle -> QueryReceived -> Lookup -> Building -> Sent +// Full lifecycle (minimal root-question packet) — Idle -> QueryReceived -> Lookup -> Building -> Sent // ========================================================================= test "full lifecycle: Idle -> QueryReceived -> Lookup -> ResponseBuilding -> Sent" { @@ -196,7 +196,26 @@ test "parse_query rejects short buffer" { test "parse_query rejects null buffer" { const slot = dns.dns_create_context(); defer dns.dns_destroy_context(slot); - try std.testing.expectEqual(@as(u8, 1), dns.dns_parse_query(slot, null, 12)); + try std.testing.expectEqual(@as(u8, 1), dns.dns_parse_query(slot, null, 17)); +} + +test "parser rejects malformed and unsupported DNS query packets" { + const slot = dns.dns_create_context(); + defer dns.dns_destroy_context(slot); + + var no_question: [17]u8 = [_]u8{0} ** 17; + try std.testing.expectEqual(@as(u8, 1), dns.dns_parse_query(slot, &no_question, 17)); + try std.testing.expectEqual(@as(u8, 0), dns.dns_state(slot)); // still Idle + + var named_query: [19]u8 = [_]u8{0} ** 19; + named_query[5] = 1; // QDCOUNT = 1 + named_query[12] = 1; // unsupported non-root QNAME: one-byte label + named_query[13] = 'a'; + named_query[14] = 0; + named_query[16] = 1; // QTYPE = A + named_query[18] = 1; // QCLASS = IN + try std.testing.expectEqual(@as(u8, 1), dns.dns_parse_query(slot, &named_query, 19)); + try std.testing.expectEqual(@as(u8, 0), dns.dns_state(slot)); // still Idle } // ========================================================================= @@ -208,8 +227,8 @@ test "set_rcode sets response code" { defer dns.dns_destroy_context(slot); // Advance to ResponseBuilding - var query_buf: [12]u8 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - _ = dns.dns_parse_query(slot, &query_buf, 12); + var query_buf: [17]u8 = .{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }; + _ = dns.dns_parse_query(slot, &query_buf, 17); _ = dns.dns_begin_lookup(slot); _ = dns.dns_begin_response(slot); @@ -226,8 +245,8 @@ test "set_rcode rejects invalid tag" { const slot = dns.dns_create_context(); defer dns.dns_destroy_context(slot); - var query_buf: [12]u8 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - _ = dns.dns_parse_query(slot, &query_buf, 12); + var query_buf: [17]u8 = .{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }; + _ = dns.dns_parse_query(slot, &query_buf, 17); _ = dns.dns_begin_lookup(slot); _ = dns.dns_begin_response(slot); @@ -249,8 +268,8 @@ test "add all 15 record types as answers" { defer dns.dns_destroy_context(slot); // Advance to ResponseBuilding - var query_buf: [12]u8 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - _ = dns.dns_parse_query(slot, &query_buf, 12); + var query_buf: [17]u8 = .{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }; + _ = dns.dns_parse_query(slot, &query_buf, 17); _ = dns.dns_begin_lookup(slot); _ = dns.dns_begin_response(slot); @@ -266,8 +285,8 @@ test "add_answer rejects invalid record type" { const slot = dns.dns_create_context(); defer dns.dns_destroy_context(slot); - var query_buf: [12]u8 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - _ = dns.dns_parse_query(slot, &query_buf, 12); + var query_buf: [17]u8 = .{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }; + _ = dns.dns_parse_query(slot, &query_buf, 17); _ = dns.dns_begin_lookup(slot); _ = dns.dns_begin_response(slot); @@ -276,6 +295,19 @@ test "add_answer rejects invalid record type" { try std.testing.expectEqual(@as(u8, 1), dns.dns_add_answer(slot, 255, 0, 300, &rdata, 4)); } +test "add_record rejects null rdata when length is nonzero" { + const slot = dns.dns_create_context(); + defer dns.dns_destroy_context(slot); + + var query_buf: [17]u8 = .{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }; + _ = dns.dns_parse_query(slot, &query_buf, 17); + _ = dns.dns_begin_lookup(slot); + _ = dns.dns_begin_response(slot); + + try std.testing.expectEqual(@as(u8, 1), dns.dns_add_answer(slot, 0, 0, 300, null, 1)); + try std.testing.expectEqual(@as(u16, 0), dns.dns_answer_count(slot)); +} + // ========================================================================= // Authority and additional sections // ========================================================================= @@ -284,8 +316,8 @@ test "add authority and additional records" { const slot = dns.dns_create_context(); defer dns.dns_destroy_context(slot); - var query_buf: [12]u8 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - _ = dns.dns_parse_query(slot, &query_buf, 12); + var query_buf: [17]u8 = .{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }; + _ = dns.dns_parse_query(slot, &query_buf, 17); _ = dns.dns_begin_lookup(slot); _ = dns.dns_begin_response(slot); @@ -297,33 +329,34 @@ test "add authority and additional records" { } // ========================================================================= -// DNSSEC state machine +// DNSSEC state machine (cryptographic operations unavailable) // ========================================================================= -test "DNSSEC enable, load key, sign" { +test "DNSSEC fails closed without key material or signing backend" { const slot = dns.dns_create_context(); defer dns.dns_destroy_context(slot); - // Advance to ResponseBuilding - var query_buf: [12]u8 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - _ = dns.dns_parse_query(slot, &query_buf, 12); + // Advance to ResponseBuilding. + var query_buf: [17]u8 = .{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }; + _ = dns.dns_parse_query(slot, &query_buf, 17); _ = dns.dns_begin_lookup(slot); _ = dns.dns_begin_response(slot); - // Disabled -> Enabled try std.testing.expectEqual(@as(u8, 0), dns.dns_enable_dnssec(slot)); - try std.testing.expectEqual(@as(u8, 1), dns.dns_dnssec_state(slot)); // enabled - - // Enabled -> KeyLoaded (Ed25519) - try std.testing.expectEqual(@as(u8, 0), dns.dns_load_dnssec_key(slot, 4)); - try std.testing.expectEqual(@as(u8, 2), dns.dns_dnssec_state(slot)); // key_loaded + try std.testing.expectEqual(@as(u8, 1), dns.dns_dnssec_state(slot)); // enabled only - // KeyLoaded -> Validated (sign response) - try std.testing.expectEqual(@as(u8, 0), dns.dns_sign_response(slot)); - try std.testing.expectEqual(@as(u8, 3), dns.dns_dnssec_state(slot)); // validated + // The API supplies no private-key bytes; failed operations do not advance state. + try std.testing.expectEqual(@as(u8, 1), dns.dns_load_dnssec_key(slot, 4)); + try std.testing.expectEqual(@as(u8, 1), dns.dns_dnssec_state(slot)); + try std.testing.expectEqual(@as(u8, 1), dns.dns_sign_response(slot)); + try std.testing.expectEqual(@as(u8, 1), dns.dns_validate_dnssec(slot)); - // Validate succeeds - try std.testing.expectEqual(@as(u8, 0), dns.dns_validate_dnssec(slot)); + // DNSSEC-enabled output must not silently downgrade to an unsigned response. + var out_buf: [512]u8 = undefined; + var out_len: u16 = 77; + try std.testing.expectEqual(@as(u8, 1), dns.dns_build_response(slot, &out_buf, &out_len)); + try std.testing.expectEqual(@as(u16, 0), out_len); + try std.testing.expectEqual(@as(u8, 3), dns.dns_state(slot)); // still ResponseBuilding } test "DNSSEC enable rejects double enable" { @@ -346,13 +379,14 @@ test "DNSSEC load key rejects invalid algorithm" { try std.testing.expectEqual(@as(u8, 1), dns.dns_load_dnssec_key(slot, 99)); } -test "DNSSEC sign requires ResponseBuilding state" { +test "DNSSEC sign rejects outside ResponseBuilding state" { const slot = dns.dns_create_context(); defer dns.dns_destroy_context(slot); _ = dns.dns_enable_dnssec(slot); - _ = dns.dns_load_dnssec_key(slot, 0); - // Still in Idle, not ResponseBuilding + try std.testing.expectEqual(@as(u8, 1), dns.dns_load_dnssec_key(slot, 0)); + // Still in Idle, not ResponseBuilding; missing signing backend also rejects. try std.testing.expectEqual(@as(u8, 1), dns.dns_sign_response(slot)); + try std.testing.expectEqual(@as(u8, 1), dns.dns_dnssec_state(slot)); } test "DNSSEC validate fails when not validated" { @@ -397,18 +431,18 @@ test "cannot parse query after Sent (terminal)" { defer dns.dns_destroy_context(slot); // Complete full lifecycle - var query_buf: [12]u8 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - _ = dns.dns_parse_query(slot, &query_buf, 12); + var query_buf: [17]u8 = .{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }; + _ = dns.dns_parse_query(slot, &query_buf, 17); _ = dns.dns_begin_lookup(slot); _ = dns.dns_begin_response(slot); var out_buf: [4096]u8 = undefined; var out_len: u16 = 0; - _ = dns.dns_build_response(slot, &out_buf, &out_len); + try std.testing.expectEqual(@as(u8, 0), dns.dns_build_response(slot, &out_buf, &out_len)); // Now in Sent state — cannot parse another query try std.testing.expectEqual(@as(u8, 4), dns.dns_state(slot)); // sent - try std.testing.expectEqual(@as(u8, 1), dns.dns_parse_query(slot, &query_buf, 12)); + try std.testing.expectEqual(@as(u8, 1), dns.dns_parse_query(slot, &query_buf, 17)); } // ========================================================================= @@ -474,16 +508,16 @@ test "built response has correct header structure" { var out_buf: [4096]u8 = undefined; var out_len: u16 = 0; - _ = dns.dns_build_response(slot, &out_buf, &out_len); + try std.testing.expectEqual(@as(u8, 0), dns.dns_build_response(slot, &out_buf, &out_len)); // Verify transaction ID try std.testing.expectEqual(@as(u8, 0xAB), out_buf[0]); try std.testing.expectEqual(@as(u8, 0xCD), out_buf[1]); - // Verify QR=1, AA=1, RD=1 - try std.testing.expectEqual(@as(u8, 0x85), out_buf[2]); - // Verify RA=1, RCODE=0 - try std.testing.expectEqual(@as(u8, 0x80), out_buf[3]); + // Verify QR=1 and RD echoed; AA is clear because this is not authoritative. + try std.testing.expectEqual(@as(u8, 0x81), out_buf[2]); + // Verify RA=0 and RCODE=0 (recursion is not implemented). + try std.testing.expectEqual(@as(u8, 0x00), out_buf[3]); // Verify QDCOUNT=1 try std.testing.expectEqual(@as(u8, 0), out_buf[4]); @@ -502,6 +536,27 @@ test "built response has correct header structure" { try std.testing.expectEqual(@as(u8, 0), out_buf[11]); } +test "oversized response is rejected before touching the output buffer" { + const slot = dns.dns_create_context(); + defer dns.dns_destroy_context(slot); + + var query_buf: [17]u8 = .{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }; + _ = dns.dns_parse_query(slot, &query_buf, 17); + _ = dns.dns_begin_lookup(slot); + _ = dns.dns_begin_response(slot); + + var rdata: [256]u8 = [_]u8{0x5A} ** 256; + try std.testing.expectEqual(@as(u8, 0), dns.dns_add_answer(slot, 0, 0, 60, &rdata, 256)); + try std.testing.expectEqual(@as(u8, 0), dns.dns_add_answer(slot, 0, 0, 60, &rdata, 256)); + + var out_buf: [512]u8 = [_]u8{0xAA} ** 512; + var out_len: u16 = 77; + try std.testing.expectEqual(@as(u8, 1), dns.dns_build_response(slot, &out_buf, &out_len)); + try std.testing.expectEqual(@as(u16, 0), out_len); + try std.testing.expectEqual(@as(u8, 3), dns.dns_state(slot)); // remains ResponseBuilding + for (out_buf) |byte| try std.testing.expectEqual(@as(u8, 0xAA), byte); +} + // ========================================================================= // Stateless transition tables // ========================================================================= diff --git a/protocols/proven-dns/generated/abi/dns.h b/protocols/proven-dns/generated/abi/dns.h index 094abad0..4a86a4f8 100644 --- a/protocols/proven-dns/generated/abi/dns.h +++ b/protocols/proven-dns/generated/abi/dns.h @@ -100,11 +100,12 @@ uint8_t dns_query_rtype(int slot); uint8_t dns_query_class(int slot); /* -- Lifecycle transitions ------------------------------------------------ */ +/* Exact 17-byte standard root-question subset (QR=0, OPCODE=QUERY); RD is the only flag. */ uint8_t dns_parse_query(int slot, const uint8_t *buf, uint16_t len); uint8_t dns_begin_lookup(int slot); uint8_t dns_begin_response(int slot); -/* -- Record addition ------------------------------------------------------ */ +/* -- Record addition (RDATA <=256 bytes; non-empty RDATA needs a valid pointer) -- */ uint8_t dns_add_answer(int slot, uint8_t rtype, uint8_t rclass, uint32_t ttl, const uint8_t *rdata, uint16_t rdlen); uint8_t dns_add_authority(int slot, uint8_t rtype, uint8_t rclass, @@ -114,9 +115,10 @@ uint8_t dns_add_additional(int slot, uint8_t rtype, uint8_t rclass, uint8_t dns_set_rcode(int slot, uint8_t rcode); /* -- Response building ---------------------------------------------------- */ +/* out requires >=512 writable bytes; no capacity parameter. Messages >512 reject before writing. */ uint8_t dns_build_response(int slot, uint8_t *out, uint16_t *out_len); -/* -- DNSSEC operations ---------------------------------------------------- */ +/* -- DNSSEC operations: cryptographic key load/sign/validation fail closed -- */ uint8_t dns_enable_dnssec(int slot); uint8_t dns_load_dnssec_key(int slot, uint8_t algo); uint8_t dns_sign_response(int slot); diff --git a/protocols/proven-dns/proven-dns.ipkg b/protocols/proven-dns/proven-dns.ipkg index 542fe166..6f9f4694 100644 --- a/protocols/proven-dns/proven-dns.ipkg +++ b/protocols/proven-dns/proven-dns.ipkg @@ -5,7 +5,7 @@ package proven-dns version = "0.1.0" authors = "Jonathan D.A. Jewell" license = "MPL-2.0" -brief = "DNS resolver that cannot crash on malformed queries" +brief = "Root-question DNS state-machine/builder skeleton; DNSSEC fails closed" sourcedir = "src" main = Main diff --git a/protocols/proven-dns/src/DNS.idr b/protocols/proven-dns/src/DNS.idr index 8855dc84..cee441f4 100644 --- a/protocols/proven-dns/src/DNS.idr +++ b/protocols/proven-dns/src/DNS.idr @@ -1,7 +1,10 @@ -- SPDX-License-Identifier: MPL-2.0 -- Copyright (c) Jonathan D.A. Jewell -- --- proven-dns: A DNS resolver implementation that cannot crash. +-- proven-dns: Typed DNS data and validation models. +-- +-- The current FFI is a minimal root-question message-builder model, not a +-- general resolver; DNSSEC cryptographic operations fail closed. -- -- Architecture: -- - Name: Length-validated domain names (max 63 per label, 253 total) diff --git a/protocols/proven-dns/src/DNSABI/Foreign.idr b/protocols/proven-dns/src/DNSABI/Foreign.idr index 92bae10e..2b5e43e2 100644 --- a/protocols/proven-dns/src/DNSABI/Foreign.idr +++ b/protocols/proven-dns/src/DNSABI/Foreign.idr @@ -3,8 +3,9 @@ -- -- DNSABI.Foreign: Foreign function declarations for the C bridge. -- --- Declares the opaque handle type and documents the complete FFI contract --- that the Zig implementation must provide. +-- Declares the opaque handle type and documents the FFI contract. DNSSEC key +-- loading, signing, and validation fail closed until cryptographic backends are +-- implemented; response construction rejects DNSSEC-enabled contexts. module DNSABI.Foreign @@ -56,7 +57,10 @@ abiVersion = 1 -- +-------------------------+---------------------------------------------+ -- | dns_parse_query | (slot: c_int, buf: *const u8, | -- | | len: u16) -> u8 (0=ok, 1=error) | --- | | Idle -> QueryReceived. | +-- | | Exact 17-byte request: QR=0, OPCODE=QUERY; | +-- | | RD is the only set flag. One root QNAME, a | +-- | | recognized QTYPE/QCLASS, and zero other | +-- | | section counts are required. | -- +-------------------------+---------------------------------------------+ -- | dns_begin_lookup | (slot: c_int) -> u8 (0=ok, 1=rejected) | -- | | QueryReceived -> Lookup. | @@ -67,7 +71,8 @@ abiVersion = 1 -- | dns_add_answer | (slot: c_int, rtype: u8, rclass: u8, | -- | | ttl: u32, rdata: *const u8, | -- | | rdlen: u16) -> u8 (0=ok, 1=rejected) | --- | | Only valid in ResponseBuilding state. | +-- | | Only in ResponseBuilding; RDATA <=256 bytes;| +-- | | non-empty RDATA requires a valid pointer. | -- +-------------------------+---------------------------------------------+ -- | dns_add_authority | (slot: c_int, rtype: u8, rclass: u8, | -- | | ttl: u32, rdata: *const u8, | @@ -82,20 +87,23 @@ abiVersion = 1 -- +-------------------------+---------------------------------------------+ -- | dns_build_response | (slot: c_int, out: *u8, out_len: *u16) | -- | | -> u8 (0=ok, 1=error) | --- | | ResponseBuilding -> Sent. | +-- | | No capacity argument: caller MUST provide | +-- | | >=512 writable bytes; larger wire messages | +-- | | are rejected before writing. | +-- | | DNSSEC-enabled contexts fail closed. | +-- | | ResponseBuilding -> Sent on success. | -- +-------------------------+---------------------------------------------+ -- | dns_enable_dnssec | (slot: c_int) -> u8 (0=ok, 1=rejected) | -- | | Disabled -> Enabled. | -- +-------------------------+---------------------------------------------+ -- | dns_load_dnssec_key | (slot: c_int, algo: u8) -> u8 | --- | | Enabled -> KeyLoaded. | +-- | | Always rejects: ABI has no private-key bytes.| -- +-------------------------+---------------------------------------------+ -- | dns_sign_response | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | KeyLoaded -> Validated. Only valid during | --- | | ResponseBuilding lifecycle state. | +-- | | Always rejects: no signing backend. | -- +-------------------------+---------------------------------------------+ -- | dns_validate_dnssec | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | Check DNSSEC validation result. | +-- | | Always rejects: no DNSSEC validator. | -- +-------------------------+---------------------------------------------+ -- | dns_answer_count | (slot: c_int) -> u16 | -- +-------------------------+---------------------------------------------+ @@ -115,5 +123,5 @@ abiVersion = 1 -- | | Stateless lifecycle transition check. | -- +-------------------------+---------------------------------------------+ -- | dns_can_dnssec_transition | (from: u8, to: u8) -> u8 (1=yes, 0=no) | --- | | Stateless DNSSEC transition check. | +-- | | Abstract model transition check only. | -- +-------------------------+---------------------------------------------+ diff --git a/protocols/proven-dns/src/DNSABI/Transitions.idr b/protocols/proven-dns/src/DNSABI/Transitions.idr index 61f28097..f2feebbb 100644 --- a/protocols/proven-dns/src/DNSABI/Transitions.idr +++ b/protocols/proven-dns/src/DNSABI/Transitions.idr @@ -1,7 +1,11 @@ -- SPDX-License-Identifier: MPL-2.0 -- Copyright (c) Jonathan D.A. Jewell -- --- DNSABI.Transitions: Valid DNS query lifecycle state transitions. +-- DNSABI.Transitions: Abstract DNS query lifecycle transitions. +-- +-- These are pure state-machine witnesses, not evidence of a running DNS +-- resolver or available DNSSEC backend. The current Zig FFI rejects DNSSEC +-- key loading, signing, and validation. -- -- Models the DNS query processing lifecycle (RFC 1035 Section 4): -- @@ -52,16 +56,16 @@ Eq DnsState where -- DNSSEC validation states --------------------------------------------------------------------------- -||| DNSSEC operational state, orthogonal to query lifecycle. +||| Abstract DNSSEC model state, orthogonal to the query lifecycle. public export data DnssecState : Type where - ||| DNSSEC is not enabled for this context. + ||| Abstract model state: DNSSEC is not enabled. DnssecDisabled : DnssecState - ||| DNSSEC is enabled but no signing key is loaded. + ||| Abstract model state: DNSSEC is enabled but no key is loaded. DnssecEnabled : DnssecState - ||| A DNSSEC signing key has been loaded. + ||| Abstract model state; the operational FFI cannot load a key. DnssecKeyLoaded : DnssecState - ||| DNSSEC validation has been performed on the response. + ||| Abstract model state; the operational FFI cannot validate DNSSEC. DnssecValidated : DnssecState public export @@ -101,11 +105,11 @@ data ValidDnsTransition : DnsState -> DnsState -> Type where ||| Proof witness that a DNSSEC state transition is valid. public export data ValidDnssecTransition : DnssecState -> DnssecState -> Type where - ||| Disabled -> Enabled (enable DNSSEC on context). + ||| Abstract transition: Disabled -> Enabled (mark mode requested). EnableDnssec : ValidDnssecTransition DnssecDisabled DnssecEnabled - ||| Enabled -> KeyLoaded (load a signing key). + ||| Abstract transition only; the FFI cannot load key material. LoadKey : ValidDnssecTransition DnssecEnabled DnssecKeyLoaded - ||| KeyLoaded -> Validated (sign and validate response). + ||| Abstract transition only; the FFI cannot sign or validate. ValidateSig : ValidDnssecTransition DnssecKeyLoaded DnssecValidated --------------------------------------------------------------------------- @@ -117,7 +121,7 @@ public export data CanAddRecord : DnsState -> Type where BuildingCanAdd : CanAddRecord ResponseBuilding -||| Proof that a context can perform DNSSEC signing. +||| Pure model witness that signing would require a loaded-key state. public export data CanSign : DnssecState -> Type where KeyLoadedCanSign : CanSign DnssecKeyLoaded diff --git a/protocols/proven-dns/src/Main.idr b/protocols/proven-dns/src/Main.idr index 9f0928be..28d08b6d 100644 --- a/protocols/proven-dns/src/Main.idr +++ b/protocols/proven-dns/src/Main.idr @@ -3,9 +3,9 @@ -- -- proven-dns: Main entry point -- --- A DNS resolver implementation that cannot crash on malformed queries. --- Uses proven's type-safe approach to ensure all name parsing, query --- construction, and response generation is total. +-- A demonstration of the typed DNS model. The current Zig FFI is deliberately +-- limited to one root-name question; it is not a general resolver, and DNSSEC +-- key loading, signing, and validation are unavailable. -- -- Usage: -- idris2 --build proven-dns.ipkg @@ -183,7 +183,7 @@ demoZone = do covering main : IO () main = do - putStrLn "proven-dns v0.1.0 -- DNS that cannot crash" + putStrLn "proven-dns v0.1.0 -- typed DNS message-model demonstration" putStrLn "Powered by proven (Idris 2 formal verification)" putStrLn "" putStrLn $ "DNS port: " ++ show (cast {to=Nat} dnsPort) diff --git a/protocols/proven-graphdb/src/Main.idr b/protocols/proven-graphdb/src/Main.idr index 9292f039..0a762ec5 100644 --- a/protocols/proven-graphdb/src/Main.idr +++ b/protocols/proven-graphdb/src/Main.idr @@ -9,7 +9,6 @@ import Graphdb %default total ||| Print server name, ports, and enumerate all type constructors. -partial main : IO () main = do putStrLn "==========================================" diff --git a/protocols/proven-kerberos/ffi/zig/src/kerberos.zig b/protocols/proven-kerberos/ffi/zig/src/kerberos.zig index c9163d3e..d7fdeea8 100644 --- a/protocols/proven-kerberos/ffi/zig/src/kerberos.zig +++ b/protocols/proven-kerberos/ffi/zig/src/kerberos.zig @@ -407,47 +407,25 @@ pub export fn krb_selected_enctype(slot: c_int) callconv(.c) u8 { // -- Authentication state transitions ----------------------------------------- -/// Simulates AS exchange: Initial -> TGTObtained. -/// Requires client principal and realm to be set. -/// Returns 0 on success, 1 if rejected. +/// AS exchange is unavailable: this ABI has no KDC connection, credentials, +/// pre-authentication, or encrypted ticket validation. Always rejects. pub export fn krb_obtain_tgt(slot: c_int) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - const idx = validSlot(slot) orelse return 1; - if (contexts[idx].auth_state != .initial) return 1; - if (!contexts[idx].client_set) return 1; - if (contexts[idx].realm_len == 0) return 1; - - contexts[idx].auth_state = .tgt_obtained; - contexts[idx].has_tgt = true; - return 0; + _ = slot; + return 1; } -/// Simulates TGS exchange: TGTObtained -> ServiceTicketObtained. -/// Requires service principal to be set. -/// Returns 0 on success, 1 if rejected. +/// TGS exchange is unavailable: this ABI has no validated TGT, KDC, or +/// encrypted service ticket. Always rejects. pub export fn krb_obtain_service_ticket(slot: c_int) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - const idx = validSlot(slot) orelse return 1; - if (contexts[idx].auth_state != .tgt_obtained) return 1; - if (!contexts[idx].service_set) return 1; - - contexts[idx].auth_state = .service_ticket_obtained; - contexts[idx].has_service_ticket = true; - return 0; + _ = slot; + return 1; } -/// Simulates AP exchange: ServiceTicketObtained -> Authenticated. -/// Returns 0 on success, 1 if rejected. +/// AP authentication is unavailable: no service ticket or authenticator is +/// accepted or verified by this model. Always rejects. pub export fn krb_authenticate(slot: c_int) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - const idx = validSlot(slot) orelse return 1; - if (contexts[idx].auth_state != .service_ticket_obtained) return 1; - - contexts[idx].auth_state = .authenticated; - return 0; + _ = slot; + return 1; } /// Forces transition to AuthFailed with the given error code. diff --git a/protocols/proven-kerberos/ffi/zig/test/kerberos_test.zig b/protocols/proven-kerberos/ffi/zig/test/kerberos_test.zig index 1af4736a..cd0cdc16 100644 --- a/protocols/proven-kerberos/ffi/zig/test/kerberos_test.zig +++ b/protocols/proven-kerberos/ffi/zig/test/kerberos_test.zig @@ -125,33 +125,23 @@ test "destroy is safe with invalid slot" { // Full authentication lifecycle: Initial -> TGT -> ServiceTicket -> Auth // ========================================================================= -test "full lifecycle: Initial -> TGTObtained -> ServiceTicketObtained -> Authenticated" { +test "AS, TGS, and AP exchanges fail closed without Kerberos backends" { const realm = "EXAMPLE.COM"; const slot = krb.krb_create(realm.ptr, realm.len); defer krb.krb_destroy(slot); - // Set client principal const client = "alice"; try std.testing.expectEqual(@as(u8, 0), krb.krb_set_client_principal(slot, client.ptr, client.len, 1)); + try std.testing.expectEqual(@as(u8, 1), krb.krb_obtain_tgt(slot)); + try std.testing.expectEqual(@as(u8, 0), krb.krb_auth_state(slot)); // Initial + try std.testing.expectEqual(@as(u8, 0), krb.krb_has_tgt(slot)); - // Initial -> TGTObtained (AS exchange) - try std.testing.expectEqual(@as(u8, 0), krb.krb_obtain_tgt(slot)); - try std.testing.expectEqual(@as(u8, 1), krb.krb_auth_state(slot)); // TGTObtained - try std.testing.expectEqual(@as(u8, 1), krb.krb_has_tgt(slot)); - - // Set service principal - const service = "krbtgt/EXAMPLE.COM"; + const service = "http/server.example.com"; try std.testing.expectEqual(@as(u8, 0), krb.krb_set_service_principal(slot, service.ptr, service.len, 2)); - - // TGTObtained -> ServiceTicketObtained (TGS exchange) - try std.testing.expectEqual(@as(u8, 0), krb.krb_obtain_service_ticket(slot)); - try std.testing.expectEqual(@as(u8, 2), krb.krb_auth_state(slot)); // ServiceTicketObtained - try std.testing.expectEqual(@as(u8, 1), krb.krb_has_service_ticket(slot)); - - // ServiceTicketObtained -> Authenticated (AP exchange) - try std.testing.expectEqual(@as(u8, 0), krb.krb_authenticate(slot)); - try std.testing.expectEqual(@as(u8, 3), krb.krb_auth_state(slot)); // Authenticated - try std.testing.expectEqual(@as(u8, 1), krb.krb_has_access(slot)); + try std.testing.expectEqual(@as(u8, 1), krb.krb_obtain_service_ticket(slot)); + try std.testing.expectEqual(@as(u8, 1), krb.krb_authenticate(slot)); + try std.testing.expectEqual(@as(u8, 0), krb.krb_has_service_ticket(slot)); + try std.testing.expectEqual(@as(u8, 0), krb.krb_has_access(slot)); } // ========================================================================= @@ -223,7 +213,7 @@ test "retry rejects if not in AuthFailed" { // Re-authentication // ========================================================================= -test "reauth from Authenticated returns to Initial" { +test "reauth rejects because no authenticated exchange can be established" { const realm = "EXAMPLE.COM"; const slot = krb.krb_create(realm.ptr, realm.len); defer krb.krb_destroy(slot); @@ -236,11 +226,11 @@ test "reauth from Authenticated returns to Initial" { _ = krb.krb_obtain_service_ticket(slot); _ = krb.krb_authenticate(slot); - try std.testing.expectEqual(@as(u8, 0), krb.krb_reauth(slot)); + try std.testing.expectEqual(@as(u8, 1), krb.krb_reauth(slot)); try std.testing.expectEqual(@as(u8, 0), krb.krb_auth_state(slot)); // Initial - try std.testing.expectEqual(@as(u8, 0), krb.krb_has_tgt(slot)); // cleared - try std.testing.expectEqual(@as(u8, 0), krb.krb_has_service_ticket(slot)); // cleared - try std.testing.expectEqual(@as(u8, 0), krb.krb_has_access(slot)); // no longer + try std.testing.expectEqual(@as(u8, 0), krb.krb_has_tgt(slot)); + try std.testing.expectEqual(@as(u8, 0), krb.krb_has_service_ticket(slot)); + try std.testing.expectEqual(@as(u8, 0), krb.krb_has_access(slot)); } test "reauth rejects if not Authenticated" { @@ -255,7 +245,7 @@ test "reauth rejects if not Authenticated" { // TGT renewal // ========================================================================= -test "renew TGT succeeds from TGTObtained" { +test "renew TGT fails closed without an obtained ticket" { const realm = "EXAMPLE.COM"; const slot = krb.krb_create(realm.ptr, realm.len); defer krb.krb_destroy(slot); @@ -264,8 +254,8 @@ test "renew TGT succeeds from TGTObtained" { _ = krb.krb_set_client_principal(slot, client.ptr, client.len, 1); _ = krb.krb_obtain_tgt(slot); - try std.testing.expectEqual(@as(u8, 0), krb.krb_renew_tgt(slot)); - try std.testing.expectEqual(@as(u8, 1), krb.krb_auth_state(slot)); // still TGTObtained + try std.testing.expectEqual(@as(u8, 1), krb.krb_renew_tgt(slot)); + try std.testing.expectEqual(@as(u8, 0), krb.krb_auth_state(slot)); // remains Initial } test "renew TGT rejects if not TGTObtained" { @@ -391,26 +381,21 @@ test "selected_enctype returns 255 before negotiation" { // Ticket flag management // ========================================================================= -test "add and query ticket flags" { +test "ticket flags cannot be added without an obtained TGT" { const realm = "EXAMPLE.COM"; const slot = krb.krb_create(realm.ptr, realm.len); defer krb.krb_destroy(slot); const client = "alice"; _ = krb.krb_set_client_principal(slot, client.ptr, client.len, 1); - _ = krb.krb_obtain_tgt(slot); + _ = krb.krb_obtain_tgt(slot); // fails closed - // No flags initially try std.testing.expectEqual(@as(u32, 0), krb.krb_ticket_flags_count(slot)); - try std.testing.expectEqual(@as(u8, 0), krb.krb_has_ticket_flag(slot, 0)); // Forwardable - - // Add Forwardable (0) and Renewable (4) - try std.testing.expectEqual(@as(u8, 0), krb.krb_add_ticket_flag(slot, 0)); - try std.testing.expectEqual(@as(u8, 0), krb.krb_add_ticket_flag(slot, 4)); - try std.testing.expectEqual(@as(u32, 2), krb.krb_ticket_flags_count(slot)); - try std.testing.expectEqual(@as(u8, 1), krb.krb_has_ticket_flag(slot, 0)); // Forwardable set - try std.testing.expectEqual(@as(u8, 1), krb.krb_has_ticket_flag(slot, 4)); // Renewable set - try std.testing.expectEqual(@as(u8, 0), krb.krb_has_ticket_flag(slot, 1)); // Forwarded not set + try std.testing.expectEqual(@as(u8, 1), krb.krb_add_ticket_flag(slot, 0)); + try std.testing.expectEqual(@as(u8, 1), krb.krb_add_ticket_flag(slot, 4)); + try std.testing.expectEqual(@as(u32, 0), krb.krb_ticket_flags_count(slot)); + try std.testing.expectEqual(@as(u8, 0), krb.krb_has_ticket_flag(slot, 0)); + try std.testing.expectEqual(@as(u8, 0), krb.krb_has_ticket_flag(slot, 4)); } test "add_ticket_flag rejects invalid flag tag" { diff --git a/protocols/proven-kerberos/proven-kerberos.ipkg b/protocols/proven-kerberos/proven-kerberos.ipkg index 374a25e4..321e6333 100644 --- a/protocols/proven-kerberos/proven-kerberos.ipkg +++ b/protocols/proven-kerberos/proven-kerberos.ipkg @@ -5,7 +5,7 @@ package proven-kerberos version = "0.1.0" authors = "Jonathan D.A. Jewell" license = "MPL-2.0" -brief = "RFC 4120 Kerberos V5 skeleton" +brief = "Kerberos principal/enctype model; no KDC, ticket validation, or encryption backend" sourcedir = "src" main = Main diff --git a/protocols/proven-kerberos/src/KerberosABI/Foreign.idr b/protocols/proven-kerberos/src/KerberosABI/Foreign.idr index f6c154b8..e029e2ef 100644 --- a/protocols/proven-kerberos/src/KerberosABI/Foreign.idr +++ b/protocols/proven-kerberos/src/KerberosABI/Foreign.idr @@ -3,14 +3,10 @@ -- -- KerberosABI.Foreign: Foreign function declarations for the C bridge. -- --- Declares the opaque handle type and documents the complete FFI contract --- that the Zig implementation (ffi/zig/src/kerberos.zig) must provide. --- --- The Zig FFI manages: --- - 64-slot mutex-protected authentication session pool --- - Ticket cache (TGTs and service tickets per session) --- - Encryption type negotiation (strongest-common-cipher selection) --- - Principal name storage and validation +-- Declares the opaque handle type and documents the Kerberos model. +-- The Zig FFI stores principal/enctype metadata only. It has no KDC, ticket +-- issuance or validation, encryption, or authenticator backend; AS, TGS, and +-- AP exchange operations fail closed. -- -- All functions use C calling convention and communicate state via -- Bits8 tags matching KerberosABI.Layout exactly. @@ -80,8 +76,8 @@ abiVersion = 1 -- | krb_negotiate_enctype | (slot: c_int, server_types_ptr: ptr, | -- | | count: u32) | -- | | -> u8 (selected enc tag, 255=failure) | --- | | Server selects strongest common cipher | --- | | from client proposal vs server list. | +-- | | Selects an enctype metadata tag only; | +-- | | no encryption or KDC exchange occurs. | -- +-------------------------------+-------------------------------------------+ -- | krb_negotiation_state | (slot: c_int) -> u8 (NegotiationState) | -- | | Returns current negotiation state. | @@ -92,18 +88,15 @@ abiVersion = 1 -- | | or 255 if not yet selected. | -- +-------------------------------+-------------------------------------------+ -- | krb_obtain_tgt | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | Simulates AS exchange: Initial -> | --- | | TGTObtained. Requires client principal | --- | | and realm to be set. | +-- | | Always rejects: no KDC, credentials, | +-- | | pre-authentication, or ticket backend. | -- +-------------------------------+-------------------------------------------+ -- | krb_obtain_service_ticket | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | Simulates TGS exchange: TGTObtained -> | --- | | ServiceTicketObtained. Requires service | --- | | principal to be set. | +-- | | Always rejects: no validated TGT or KDC. | -- +-------------------------------+-------------------------------------------+ -- | krb_authenticate | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | Simulates AP exchange: | --- | | ServiceTicketObtained -> Authenticated. | +-- | | Always rejects: no service ticket or | +-- | | authenticator is verified. | -- +-------------------------------+-------------------------------------------+ -- | krb_fail | (slot: c_int, error_code: u8) | -- | | -> u8 (0=ok, 1=rejected) | @@ -116,23 +109,20 @@ abiVersion = 1 -- | | Clears tickets and negotiation state. | -- +-------------------------------+-------------------------------------------+ -- | krb_renew_tgt | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | Renews TGT: TGTObtained -> TGTObtained. | --- | | Resets ticket lifetime. | +-- | | Always rejects: no real TGT is issued. | -- +-------------------------------+-------------------------------------------+ -- | krb_reauth | (slot: c_int) -> u8 (0=ok, 1=rejected) | --- | | Re-authenticate: Authenticated -> | --- | | Initial. Clears all tickets. | +-- | | Requires modeled Authenticated state, | +-- | | unreachable without a KDC backend. | -- +-------------------------------+-------------------------------------------+ -- | krb_has_tgt | (slot: c_int) -> u8 (1=yes, 0=no) | --- | | Whether the session holds a valid TGT. | +-- | | Model flag only; always false without a KDC. | -- +-------------------------------+-------------------------------------------+ -- | krb_has_service_ticket | (slot: c_int) -> u8 (1=yes, 0=no) | --- | | Whether the session holds a service | --- | | ticket. | +-- | | Model flag only; always false without TGS. | -- +-------------------------------+-------------------------------------------+ -- | krb_has_access | (slot: c_int) -> u8 (1=yes, 0=no) | --- | | Whether the session is fully | --- | | authenticated. | +-- | | Model flag only; always false without AP verification. | -- +-------------------------------+-------------------------------------------+ -- | krb_last_error | (slot: c_int) -> u8 (ErrorCode tag) | -- | | Returns the last error code set by | @@ -144,11 +134,11 @@ abiVersion = 1 -- +-------------------------------+-------------------------------------------+ -- | krb_add_ticket_flag | (slot: c_int, flag: u8) | -- | | -> u8 (0=ok, 1=rejected) | --- | | Adds a flag to the TGT. Requires | --- | | TGTObtained state. | +-- | | Adds modeled flag metadata only; | +-- | | TGTObtained is unreachable in this FFI. | -- +-------------------------------+-------------------------------------------+ -- | krb_has_ticket_flag | (slot: c_int, flag: u8) -> u8 (1/0) | --- | | Whether the TGT has a specific flag. | +-- | | Whether the model contains this flag. | -- +-------------------------------+-------------------------------------------+ -- | krb_can_transition | (from: u8, to: u8) -> u8 (1=yes, 0=no) | -- | | Stateless: checks if an auth state | diff --git a/protocols/proven-kerberos/src/Main.idr b/protocols/proven-kerberos/src/Main.idr index dfae571b..df150563 100644 --- a/protocols/proven-kerberos/src/Main.idr +++ b/protocols/proven-kerberos/src/Main.idr @@ -36,7 +36,7 @@ allTicketFlags = main : IO () main = do - putStrLn "proven-kerberos: RFC 4120 Kerberos V5" + putStrLn "proven-kerberos: Kerberos principal/enctype model (no KDC or ticket backend)" putStrLn $ " KDC port: " ++ show kdcPort putStrLn $ " kpasswd port: " ++ show kpasswdPort putStrLn $ " Ticket lifetime: " ++ show defaultTicketLifetime ++ "s" diff --git a/protocols/proven-ldp/src/Main.idr b/protocols/proven-ldp/src/Main.idr index 2da0a058..643a31d0 100644 --- a/protocols/proven-ldp/src/Main.idr +++ b/protocols/proven-ldp/src/Main.idr @@ -9,7 +9,6 @@ import Ldp %default total ||| Print server name, port, and enumerate all type constructors. -partial main : IO () main = do putStrLn "==========================================" diff --git a/protocols/proven-media/src/Main.idr b/protocols/proven-media/src/Main.idr index ce45ace7..74f4102b 100644 --- a/protocols/proven-media/src/Main.idr +++ b/protocols/proven-media/src/Main.idr @@ -9,7 +9,6 @@ import Media %default total ||| Print server name, ports, and enumerate all type constructors. -partial main : IO () main = do putStrLn "==========================================" diff --git a/protocols/proven-nesy/ffi/zig/src/nesy.zig b/protocols/proven-nesy/ffi/zig/src/nesy.zig index fb1ef2fe..711c198c 100644 --- a/protocols/proven-nesy/ffi/zig/src/nesy.zig +++ b/protocols/proven-nesy/ffi/zig/src/nesy.zig @@ -300,8 +300,9 @@ pub export fn nesy_add_proof( return 1; } -/// Verify a proof obligation by index. Returns ProofStatus tag. -/// Transitions Ready -> Verifying -> Ready, simulating proof success. +/// Verify a proof obligation by index. No proof checker or proof bytes are +/// connected to this model, so valid-looking metadata must never become Proved. +/// Returns Failed and leaves the obligation untrusted. pub export fn nesy_verify_proof(slot: c_int, index: u32) callconv(.c) u8 { mutex.lock(); defer mutex.unlock(); @@ -313,10 +314,9 @@ pub export fn nesy_verify_proof(slot: c_int, index: u32) callconv(.c) u8 { if (index >= MAX_PROOFS) return @intFromEnum(ProofStatus.failed); if (!sessions[idx].proofs[index].active) return @intFromEnum(ProofStatus.failed); - // Simulate proof verification: mark as proved. - sessions[idx].proofs[index].status = .proved; + sessions[idx].proofs[index].status = .failed; sessions[idx].state = .ready; - return @intFromEnum(ProofStatus.proved); + return @intFromEnum(ProofStatus.failed); } /// Returns the number of active proof obligations. @@ -327,20 +327,11 @@ pub export fn nesy_proof_count(slot: c_int) callconv(.c) u32 { return sessions[idx].proof_count; } -/// Detect drift between neural and symbolic results. -/// Returns DriftKind tag. May transition to Drift state on non-trivial drift. +/// Drift detection has no connected symbolic/neural results. Return 255 as an +/// unavailable sentinel (not a DriftKind tag); callers must treat it as unknown. pub export fn nesy_detect_drift(slot: c_int) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - - const idx = validSlot(slot) orelse return @intFromEnum(DriftKind.no_drift); - if (sessions[idx].state != .ready and sessions[idx].state != .reasoning) { - return @intFromEnum(DriftKind.no_drift); - } - - // Simulated drift detection: no drift by default. - sessions[idx].last_drift = .no_drift; - return @intFromEnum(DriftKind.no_drift); + _ = slot; + return 255; } /// Resolve drift, returning to Ready state. Drift -> Ready. diff --git a/protocols/proven-nesy/ffi/zig/test/integration_test.zig b/protocols/proven-nesy/ffi/zig/test/integration_test.zig index 3fdc30a2..ee4b8480 100644 --- a/protocols/proven-nesy/ffi/zig/test/integration_test.zig +++ b/protocols/proven-nesy/ffi/zig/test/integration_test.zig @@ -144,8 +144,8 @@ test "add_proof and verify_proof cycle" { )); try std.testing.expectEqual(@as(u32, 1), nesy.nesy_proof_count(slot)); - // Verify the proof - try std.testing.expectEqual(@as(u8, 2), nesy.nesy_verify_proof(slot, 0)); // Proved + // No external proof checker is connected; the obligation stays untrusted. + try std.testing.expectEqual(@as(u8, 3), nesy.nesy_verify_proof(slot, 0)); // Failed/unavailable } test "add_proof rejects invalid constraint kind" { @@ -162,11 +162,11 @@ test "add_proof rejects invalid constraint kind" { // Drift detection // ========================================================================= -test "detect_drift returns no_drift by default" { +test "detect_drift reports unavailable rather than a false no-drift result" { const slot = nesy.nesy_create(0); defer nesy.nesy_destroy(slot); - try std.testing.expectEqual(@as(u8, 0), nesy.nesy_detect_drift(slot)); // NoDrift + try std.testing.expectEqual(@as(u8, 255), nesy.nesy_detect_drift(slot)); } test "resolve_drift rejected from non-Drift state" { diff --git a/protocols/proven-nesy/proven-nesy.ipkg b/protocols/proven-nesy/proven-nesy.ipkg index 6ef968c3..6069124e 100644 --- a/protocols/proven-nesy/proven-nesy.ipkg +++ b/protocols/proven-nesy/proven-nesy.ipkg @@ -8,7 +8,7 @@ package proven-nesy version = 0.1.0 authors = "Jonathan D.A. Jewell" -brief = "Neurosymbolic integration server — formal ABI types" +brief = "Neurosymbolic state/type model; no connected proof checker or drift detector" sourcedir = "src" main = Main diff --git a/protocols/proven-nesy/src/Main.idr b/protocols/proven-nesy/src/Main.idr index 260b286f..f652d53c 100644 --- a/protocols/proven-nesy/src/Main.idr +++ b/protocols/proven-nesy/src/Main.idr @@ -19,4 +19,4 @@ main = do putStrLn $ " NeuralBackends: " ++ show [LocalModel, Claude, Gemini, Mistral, GPT, CustomNeural] putStrLn $ " Confidence: " ++ show [Verified, HighNeural, MediumNeural, LowNeural, Unknown, Contradicted] putStrLn $ " DriftKinds: " ++ show [NoDrift, SemanticDrift, ConfidenceDrift, FactualDrift, TemporalDrift, CatastrophicDrift] - putStrLn "All types total, all Show instances verified." + putStrLn "This output demonstrates model constructors only; no proof checker is connected." diff --git a/protocols/proven-nesy/src/NeSyABI/Foreign.idr b/protocols/proven-nesy/src/NeSyABI/Foreign.idr index 4c4f5d7a..b84b1a0e 100644 --- a/protocols/proven-nesy/src/NeSyABI/Foreign.idr +++ b/protocols/proven-nesy/src/NeSyABI/Foreign.idr @@ -70,12 +70,12 @@ abiVersion = 1 -- +-----------------------------+-------------------------------------------+ -- | nesy_verify_proof | (slot: c_int, index: u32) | -- | | -> u8 (ProofStatus tag) | --- | | Ready -> Verifying -> Ready. | +-- | | Returns Failed; no proof checker linked. | -- +-----------------------------+-------------------------------------------+ -- | nesy_proof_count | (slot: c_int) -> u32 | -- +-----------------------------+-------------------------------------------+ -- | nesy_detect_drift | (slot: c_int) -> u8 (DriftKind tag) | --- | | May transition to Drift state. | +-- | | Returns 255 (unavailable; not a tag). | -- +-----------------------------+-------------------------------------------+ -- | nesy_resolve_drift | (slot: c_int) -> u8 (0=ok, 1=rejected) | -- | | Drift -> Ready. | diff --git a/protocols/proven-odns/ffi/zig/src/odns.zig b/protocols/proven-odns/ffi/zig/src/odns.zig index 8a32fed3..b29eff66 100644 --- a/protocols/proven-odns/ffi/zig/src/odns.zig +++ b/protocols/proven-odns/ffi/zig/src/odns.zig @@ -3,14 +3,10 @@ // // odns.zig -- Zig FFI implementation of proven-odns. // -// Implements an Oblivious DNS (draft-pauly-dprive-oblivious-doh) session -// state machine with: -// - 64-slot mutex-protected session pool -// - HPKE key pair management (simulated) -// - Query/response encapsulation tracking -// - Role-based access enforcement (Client/Proxy/Target) -// - Query counter for statistics -// - Thread-safe via per-pool mutex +// Implements an Oblivious DNS session-state model, not an encrypted DNS +// implementation. There is no HPKE backend, DNS transport, encrypted query +// processing, or response decryption; those operations fail closed. +// The remaining ABI models session creation, roles, cleanup, and counters. // // All exported functions use C calling convention (callconv(.c)) and // communicate state via u8 tags matching ODNSABI.Types.idr exactly. @@ -159,58 +155,37 @@ pub export fn odns_state(slot: c_int) callconv(.c) u8 { return @intFromEnum(sessions[idx].state); } -/// Complete HPKE key exchange. Returns 0 on success, 1 on rejection. -/// Transitions: KeyExchange -> Ready. +/// HPKE key exchange is unavailable because no reviewed HPKE backend is linked. +/// Always returns 1 and leaves the session in KeyExchange. pub export fn odns_key_exchange( slot: c_int, pubkey_ptr: [*]const u8, pubkey_len: u32, ) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - - const idx = validSlot(slot) orelse return 1; - if (sessions[idx].state != .key_exchange) return 1; - if (pubkey_len == 0 or pubkey_len > MAX_PUBKEY_LEN) return 1; - - @memcpy(sessions[idx].pubkey[0..pubkey_len], pubkey_ptr[0..pubkey_len]); - sessions[idx].pubkey_len = pubkey_len; - sessions[idx].state = .ready; - return 0; + _ = slot; + _ = pubkey_ptr; + _ = pubkey_len; + return 1; } -/// Submit an oblivious DNS query. Returns 0 on success, 1 on rejection. -/// Transitions: Ready -> Processing. +/// Encrypted query submission is unavailable without HPKE and DNS transport. +/// Always rejects without changing session state. pub export fn odns_submit_query( slot: c_int, query_ptr: [*]const u8, query_len: u32, ) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - + _ = slot; _ = query_ptr; - - const idx = validSlot(slot) orelse return 1; - if (sessions[idx].state != .ready) return 1; - if (query_len == 0 or query_len > MAX_QUERY_LEN) return 1; - - sessions[idx].state = .processing; - return 0; + _ = query_len; + return 1; } -/// Get query response (simulated). Returns 0 on success, 1 on failure. -/// Transitions: Processing -> Ready. +/// Response retrieval and decryption are unavailable without an HPKE backend. +/// Always rejects and never increments the processed-query counter. pub export fn odns_get_response(slot: c_int) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - - const idx = validSlot(slot) orelse return 1; - if (sessions[idx].state != .processing) return 1; - - sessions[idx].query_count += 1; - sessions[idx].state = .ready; - return 0; + _ = slot; + return 1; } /// Returns the role tag for this session. diff --git a/protocols/proven-odns/ffi/zig/test/integration_test.zig b/protocols/proven-odns/ffi/zig/test/integration_test.zig index e6dce310..73d540ab 100644 --- a/protocols/proven-odns/ffi/zig/test/integration_test.zig +++ b/protocols/proven-odns/ffi/zig/test/integration_test.zig @@ -111,15 +111,15 @@ test "destroy is safe with invalid slot" { // Key exchange // ========================================================================= -test "key_exchange transitions KeyExchange -> Ready" { +test "key_exchange fails closed without HPKE and leaves session unready" { const config = "cfg"; const slot = odns.odns_create(0, config.ptr, config.len); defer odns.odns_destroy(slot); - const pubkey = "fake-public-key-32bytes-padding!"; - try std.testing.expectEqual(@as(u8, 0), odns.odns_key_exchange(slot, pubkey.ptr, pubkey.len)); - try std.testing.expectEqual(@as(u8, 2), odns.odns_state(slot)); // Ready - try std.testing.expectEqual(@as(u8, 1), odns.odns_is_ready(slot)); + const pubkey = "not-a-validated-hpke-key"; + try std.testing.expectEqual(@as(u8, 1), odns.odns_key_exchange(slot, pubkey.ptr, pubkey.len)); + try std.testing.expectEqual(@as(u8, 1), odns.odns_state(slot)); // KeyExchange + try std.testing.expectEqual(@as(u8, 0), odns.odns_is_ready(slot)); } test "key_exchange rejects empty pubkey" { @@ -131,65 +131,42 @@ test "key_exchange rejects empty pubkey" { try std.testing.expectEqual(@as(u8, 1), odns.odns_key_exchange(slot, pubkey.ptr, 0)); } -test "key_exchange rejects from Ready state" { - const config = "cfg"; - const slot = odns.odns_create(0, config.ptr, config.len); - defer odns.odns_destroy(slot); - - const pubkey = "key-data"; - _ = odns.odns_key_exchange(slot, pubkey.ptr, pubkey.len); - try std.testing.expectEqual(@as(u8, 1), odns.odns_key_exchange(slot, pubkey.ptr, pubkey.len)); -} - // ========================================================================= // Query / Response lifecycle // ========================================================================= -test "submit_query transitions Ready -> Processing" { +test "query submission fails closed when no encrypted session can be established" { const config = "cfg"; const slot = odns.odns_create(0, config.ptr, config.len); defer odns.odns_destroy(slot); const pubkey = "key"; + const query = "query"; _ = odns.odns_key_exchange(slot, pubkey.ptr, pubkey.len); - - const query = "encrypted-dns-query"; - try std.testing.expectEqual(@as(u8, 0), odns.odns_submit_query(slot, query.ptr, query.len)); - try std.testing.expectEqual(@as(u8, 3), odns.odns_state(slot)); // Processing + try std.testing.expectEqual(@as(u8, 1), odns.odns_submit_query(slot, query.ptr, query.len)); + try std.testing.expectEqual(@as(u8, 1), odns.odns_state(slot)); // remains KeyExchange } -test "get_response transitions Processing -> Ready" { +test "response retrieval fails closed without a processed encrypted query" { const config = "cfg"; const slot = odns.odns_create(0, config.ptr, config.len); defer odns.odns_destroy(slot); - const pubkey = "key"; - _ = odns.odns_key_exchange(slot, pubkey.ptr, pubkey.len); - - const query = "query"; - _ = odns.odns_submit_query(slot, query.ptr, query.len); - try std.testing.expectEqual(@as(u8, 0), odns.odns_get_response(slot)); - try std.testing.expectEqual(@as(u8, 2), odns.odns_state(slot)); // Ready + try std.testing.expectEqual(@as(u8, 1), odns.odns_get_response(slot)); + try std.testing.expectEqual(@as(u32, 0), odns.odns_query_count(slot)); } -test "query_count increments per response" { +test "rejected operations never increment query_count" { const config = "cfg"; const slot = odns.odns_create(0, config.ptr, config.len); defer odns.odns_destroy(slot); const pubkey = "key"; - _ = odns.odns_key_exchange(slot, pubkey.ptr, pubkey.len); - - try std.testing.expectEqual(@as(u32, 0), odns.odns_query_count(slot)); - const query = "q1"; + _ = odns.odns_key_exchange(slot, pubkey.ptr, pubkey.len); _ = odns.odns_submit_query(slot, query.ptr, query.len); _ = odns.odns_get_response(slot); - try std.testing.expectEqual(@as(u32, 1), odns.odns_query_count(slot)); - - _ = odns.odns_submit_query(slot, query.ptr, query.len); - _ = odns.odns_get_response(slot); - try std.testing.expectEqual(@as(u32, 2), odns.odns_query_count(slot)); + try std.testing.expectEqual(@as(u32, 0), odns.odns_query_count(slot)); } test "get_format returns HPKE" { @@ -203,13 +180,11 @@ test "get_format returns HPKE" { // Close / Cleanup // ========================================================================= -test "close transitions Ready -> Closing" { +test "close transitions KeyExchange -> Closing" { const config = "cfg"; const slot = odns.odns_create(0, config.ptr, config.len); defer odns.odns_destroy(slot); - const pubkey = "key"; - _ = odns.odns_key_exchange(slot, pubkey.ptr, pubkey.len); try std.testing.expectEqual(@as(u8, 0), odns.odns_close(slot)); try std.testing.expectEqual(@as(u8, 4), odns.odns_state(slot)); // Closing } @@ -219,8 +194,6 @@ test "cleanup transitions Closing -> Idle" { const slot = odns.odns_create(0, config.ptr, config.len); defer odns.odns_destroy(slot); - const pubkey = "key"; - _ = odns.odns_key_exchange(slot, pubkey.ptr, pubkey.len); _ = odns.odns_close(slot); try std.testing.expectEqual(@as(u8, 0), odns.odns_cleanup(slot)); try std.testing.expectEqual(@as(u8, 0), odns.odns_state(slot)); // Idle diff --git a/protocols/proven-odns/proven-odns.ipkg b/protocols/proven-odns/proven-odns.ipkg index daada8e3..38fdbe73 100644 --- a/protocols/proven-odns/proven-odns.ipkg +++ b/protocols/proven-odns/proven-odns.ipkg @@ -5,7 +5,7 @@ package proven-odns version = "0.1.0" authors = "Jonathan D.A. Jewell" license = "MPL-2.0" -brief = "Formally verified Oblivious DNS types (draft-pauly-dprive-oblivious-doh)" +brief = "Oblivious DNS session-state model; no HPKE or encrypted DNS backend" sourcedir = "src" main = Main diff --git a/protocols/proven-odns/src/Main.idr b/protocols/proven-odns/src/Main.idr index 90318081..d8ca503f 100644 --- a/protocols/proven-odns/src/Main.idr +++ b/protocols/proven-odns/src/Main.idr @@ -10,7 +10,7 @@ import ODNS covering main : IO () main = do - putStrLn "proven-odns v0.1.0 -- Formally verified Oblivious DNS types (draft-pauly-dprive-oblivious-doh)" + putStrLn "proven-odns v0.1.0 -- Oblivious DNS session-state model (no HPKE backend)" putStrLn "Powered by proven (Idris 2 formal verification)" putStrLn "" putStrLn "Roles:" diff --git a/protocols/proven-odns/src/ODNSABI/Foreign.idr b/protocols/proven-odns/src/ODNSABI/Foreign.idr index 2c7c3503..ffe6be2c 100644 --- a/protocols/proven-odns/src/ODNSABI/Foreign.idr +++ b/protocols/proven-odns/src/ODNSABI/Foreign.idr @@ -6,12 +6,10 @@ -- Declares the opaque handle type and documents the complete FFI contract -- that the Zig implementation (ffi/zig/src/odns.zig) must provide. -- --- The Zig FFI manages: --- - 64-slot mutex-protected session pool --- - HPKE key pair management --- - Query/response encapsulation tracking --- - Role-based access enforcement (Client/Proxy/Target) --- - Nonce management for replay protection +-- The Zig FFI is a state-machine model, not an Oblivious DNS implementation: +-- it has no HPKE cryptography, encrypted request/response processing, DNS +-- transport, or replay protection. Key exchange and query operations reject +-- until a reviewed protocol backend is integrated. -- -- All functions use C calling convention and communicate state via -- Bits8 tags matching ODNSABI.Types exactly. @@ -59,16 +57,14 @@ abiVersion = 1 -- +-----------------------------+-------------------------------------------+ -- | odns_key_exchange | (slot: c_int, pubkey_ptr: ptr, | -- | | pubkey_len: u32) -> u8 | --- | | (0=ok, 1=rejected) | --- | | Transitions KeyExchange -> Ready. | +-- | | Always rejects until HPKE is integrated. | -- +-----------------------------+-------------------------------------------+ -- | odns_submit_query | (slot: c_int, query_ptr: ptr, | -- | | query_len: u32) -> u8 | --- | | Transitions Ready -> Processing. | +-- | | Always rejects until encrypted transport. | -- +-----------------------------+-------------------------------------------+ -- | odns_get_response | (slot: c_int) -> u8 | --- | | Returns response status, | --- | | transitions Processing -> Ready. | +-- | | Always rejects until HPKE decryption. | -- +-----------------------------+-------------------------------------------+ -- | odns_get_role | (slot: c_int) -> u8 (Role tag) | -- +-----------------------------+-------------------------------------------+ diff --git a/protocols/proven-pqc/ffi/zig/src/pqc.zig b/protocols/proven-pqc/ffi/zig/src/pqc.zig index 2b00e22a..daa2c609 100644 --- a/protocols/proven-pqc/ffi/zig/src/pqc.zig +++ b/protocols/proven-pqc/ffi/zig/src/pqc.zig @@ -3,13 +3,13 @@ // // pqc.zig -- Zig FFI implementation of proven-pqc. // -// Implements verified Post-Quantum Cryptography key lifecycle with: +// Implements a PQC lifecycle and negotiation model, not cryptography: // - Slot-based context management (up to 64 concurrent) // - Key lifecycle state machine matching Idris2 Transitions.idr // - Hybrid negotiation state machine (classical + PQC selection) -// - Algorithm/NIST-level validation per Layout.idr tables -// - Category-aware operation validation (KEM vs Signature) -// - Thread-safe via mutex +// - Algorithm/NIST-level metadata validation per Layout.idr tables +// - Cryptographic operations fail closed until a reviewed backend exists +// - Thread-safe lifecycle state via mutex const std = @import("std"); @@ -314,67 +314,53 @@ pub export fn pqc_compromise_key(slot: c_int) callconv(.c) u8 { return 0; } -// -- Crypto operations (simulated) -------------------------------------------- +// -- Cryptographic operations ------------------------------------------------- +// No reviewed PQC implementation is linked into this FFI. These entry points +// fail closed: they never claim success, never write fabricated output, and +// clear any caller-provided output length before returning. -/// Encapsulate. Requires Active key state and KEM algorithm. -/// Returns 0=ok, 1=rejected. +/// Encapsulation is unavailable until a reviewed KEM backend is integrated. +/// Returns 1 (rejected/unavailable); output lengths are always reset to zero. pub export fn pqc_encapsulate(slot: c_int, ct: ?[*]u8, ct_len: ?*u32, ss: ?[*]u8, ss_len: ?*u32) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - const idx = validSlot(slot) orelse return 1; - if (contexts[idx].key_state != .active) return 1; - if (algorithmCategoryFromTag(contexts[idx].algorithm) != 0) return 1; // Not KEM + _ = slot; _ = ct; _ = ss; - // Simulated: write placeholder lengths. - if (ct_len) |p| p.* = 32; - if (ss_len) |p| p.* = 32; - return 0; + if (ct_len) |p| p.* = 0; + if (ss_len) |p| p.* = 0; + return 1; } -/// Decapsulate. Requires Active key state and KEM algorithm. -/// Returns 0=ok, 1=rejected. +/// Decapsulation is unavailable until a reviewed KEM backend is integrated. +/// Returns 1 (rejected/unavailable); the output length is reset to zero. pub export fn pqc_decapsulate(slot: c_int, ct: ?[*]const u8, ct_len: u32, ss: ?[*]u8, ss_len: ?*u32) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - const idx = validSlot(slot) orelse return 1; - if (contexts[idx].key_state != .active) return 1; - if (algorithmCategoryFromTag(contexts[idx].algorithm) != 0) return 1; + _ = slot; _ = ct; _ = ct_len; _ = ss; - if (ss_len) |p| p.* = 32; - return 0; + if (ss_len) |p| p.* = 0; + return 1; } -/// Sign. Requires Active key state and Signature algorithm. -/// Returns 0=ok, 1=rejected. +/// Signing is unavailable until a reviewed signature backend is integrated. +/// Returns 1 (rejected/unavailable); the output length is reset to zero. pub export fn pqc_sign(slot: c_int, msg: ?[*]const u8, msg_len: u32, sig: ?[*]u8, sig_len: ?*u32) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - const idx = validSlot(slot) orelse return 1; - if (contexts[idx].key_state != .active) return 1; - if (algorithmCategoryFromTag(contexts[idx].algorithm) != 1) return 1; // Not Signature + _ = slot; _ = msg; _ = msg_len; _ = sig; - if (sig_len) |p| p.* = 64; - return 0; + if (sig_len) |p| p.* = 0; + return 1; } -/// Verify. Requires Active key state and Signature algorithm. -/// Returns 0=ok, 1=rejected. +/// Verification is unavailable until a reviewed signature backend is integrated. +/// Always returns 1 (rejected/unavailable); no signature is accepted. pub export fn pqc_verify(slot: c_int, msg: ?[*]const u8, msg_len: u32, sig: ?[*]const u8, sig_len: u32) callconv(.c) u8 { - mutex.lock(); - defer mutex.unlock(); - const idx = validSlot(slot) orelse return 1; - if (contexts[idx].key_state != .active) return 1; - if (algorithmCategoryFromTag(contexts[idx].algorithm) != 1) return 1; + _ = slot; _ = msg; _ = msg_len; _ = sig; _ = sig_len; - return 0; + return 1; } // -- Hybrid negotiation ------------------------------------------------------- diff --git a/protocols/proven-pqc/ffi/zig/test/pqc_test.zig b/protocols/proven-pqc/ffi/zig/test/pqc_test.zig index 503bf718..bb5c7412 100644 --- a/protocols/proven-pqc/ffi/zig/test/pqc_test.zig +++ b/protocols/proven-pqc/ffi/zig/test/pqc_test.zig @@ -256,7 +256,7 @@ test "finish_keygen rejects zero-length keys" { // Crypto operations: category enforcement // ========================================================================= -test "encapsulate succeeds with KEM algorithm in Active state" { +test "encapsulation fails closed for an Active KEM context and clears lengths" { const slot = pqc.pqc_create_context(0, 0); // Kyber (KEM) defer pqc.pqc_destroy_context(slot); @@ -266,13 +266,15 @@ test "encapsulate succeeds with KEM algorithm in Active state" { _ = pqc.pqc_finish_keygen(slot, pk.ptr, pk.len, sk.ptr, sk.len); _ = pqc.pqc_activate_key(slot); - var ct_len: u32 = 0; - var ss_len: u32 = 0; - try std.testing.expectEqual(@as(u8, 0), pqc.pqc_encapsulate(slot, null, &ct_len, null, &ss_len)); + var ct_len: u32 = 99; + var ss_len: u32 = 99; + try std.testing.expectEqual(@as(u8, 1), pqc.pqc_encapsulate(slot, null, &ct_len, null, &ss_len)); + try std.testing.expectEqual(@as(u32, 0), ct_len); + try std.testing.expectEqual(@as(u32, 0), ss_len); } -test "sign rejects with KEM algorithm" { - const slot = pqc.pqc_create_context(0, 0); // Kyber (KEM) +test "signature operations fail closed for an Active signature context" { + const slot = pqc.pqc_create_context(1, 1); // Dilithium (Signature) defer pqc.pqc_destroy_context(slot); _ = pqc.pqc_begin_keygen(slot); @@ -281,22 +283,16 @@ test "sign rejects with KEM algorithm" { _ = pqc.pqc_finish_keygen(slot, pk.ptr, pk.len, sk.ptr, sk.len); _ = pqc.pqc_activate_key(slot); - var sig_len: u32 = 0; + var sig_len: u32 = 99; try std.testing.expectEqual(@as(u8, 1), pqc.pqc_sign(slot, null, 0, null, &sig_len)); + try std.testing.expectEqual(@as(u32, 0), sig_len); + try std.testing.expectEqual(@as(u8, 1), pqc.pqc_verify(slot, null, 0, null, 0)); } -test "sign succeeds with Signature algorithm in Active state" { - const slot = pqc.pqc_create_context(1, 1); // Dilithium (Signature) - defer pqc.pqc_destroy_context(slot); - - _ = pqc.pqc_begin_keygen(slot); - const pk = "pk"; - const sk = "sk"; - _ = pqc.pqc_finish_keygen(slot, pk.ptr, pk.len, sk.ptr, sk.len); - _ = pqc.pqc_activate_key(slot); - - var sig_len: u32 = 0; - try std.testing.expectEqual(@as(u8, 0), pqc.pqc_sign(slot, null, 0, null, &sig_len)); +test "decapsulation fails closed and clears output length" { + var ss_len: u32 = 99; + try std.testing.expectEqual(@as(u8, 1), pqc.pqc_decapsulate(-1, null, 0, null, &ss_len)); + try std.testing.expectEqual(@as(u32, 0), ss_len); } test "encapsulate rejects with Signature algorithm" { diff --git a/protocols/proven-pqc/proven-pqc.ipkg b/protocols/proven-pqc/proven-pqc.ipkg index 01d60403..bafa7554 100644 --- a/protocols/proven-pqc/proven-pqc.ipkg +++ b/protocols/proven-pqc/proven-pqc.ipkg @@ -5,7 +5,7 @@ package proven-pqc version = "0.1.0" authors = "Jonathan D.A. Jewell" license = "MPL-2.0" -brief = "Post-Quantum Cryptography server with NIST FIPS 203/204/205 algorithms" +brief = "PQC lifecycle/negotiation model; cryptographic operations fail closed without a backend" sourcedir = "src" main = Main diff --git a/protocols/proven-pqc/src/Main.idr b/protocols/proven-pqc/src/Main.idr index 0fea59f4..2a15e63e 100644 --- a/protocols/proven-pqc/src/Main.idr +++ b/protocols/proven-pqc/src/Main.idr @@ -36,7 +36,7 @@ allOperations = [KeyGen, Encapsulate, Decapsulate, Sign, Verify] main : IO () main = do - putStrLn "proven-pqc : Post-Quantum Cryptography server" + putStrLn "proven-pqc : PQC lifecycle/negotiation model (cryptography unavailable)" putStrLn $ " Default KEM: " ++ defaultKEM putStrLn $ " Default Sig: " ++ defaultSig putStrLn $ " Default Hybrid Mode: " ++ show defaultHybridMode diff --git a/protocols/proven-pqc/src/PQCABI/Foreign.idr b/protocols/proven-pqc/src/PQCABI/Foreign.idr index 480063bb..b2d433c7 100644 --- a/protocols/proven-pqc/src/PQCABI/Foreign.idr +++ b/protocols/proven-pqc/src/PQCABI/Foreign.idr @@ -78,19 +78,19 @@ abiVersion = 1 -- +-------------------------+---------------------------------------------+ -- | pqc_encapsulate | (slot: c_int, ct: *u8, ct_len: *u32, | -- | | ss: *u8, ss_len: *u32) -> u8 | --- | | Requires Active key state, KEM algorithm. | +-- | | Unavailable: returns 1; output lengths 0. | -- +-------------------------+---------------------------------------------+ -- | pqc_decapsulate | (slot: c_int, ct: *const u8, ct_len: u32, | -- | | ss: *u8, ss_len: *u32) -> u8 | --- | | Requires Active key state, KEM algorithm. | +-- | | Unavailable: returns 1; output length 0. | -- +-------------------------+---------------------------------------------+ -- | pqc_sign | (slot: c_int, msg: *const u8, msg_len: u32, | -- | | sig: *u8, sig_len: *u32) -> u8 | --- | | Requires Active key state, sig algorithm. | +-- | | Unavailable: returns 1; output length 0. | -- +-------------------------+---------------------------------------------+ -- | pqc_verify | (slot: c_int, msg: *const u8, msg_len: u32, | -- | | sig: *const u8, sig_len: u32) -> u8 | --- | | Requires Active key state, sig algorithm. | +-- | | Always returns 1 (unavailable/rejected). | -- +-------------------------+---------------------------------------------+ -- | pqc_set_hybrid_mode | (slot: c_int, mode: u8) -> u8 | -- | | Set the hybrid mode for this context. | diff --git a/protocols/proven-tacacs/ffi/zig/src/tacacs.zig b/protocols/proven-tacacs/ffi/zig/src/tacacs.zig index e28997cf..f3ceda70 100644 --- a/protocols/proven-tacacs/ffi/zig/src/tacacs.zig +++ b/protocols/proven-tacacs/ffi/zig/src/tacacs.zig @@ -251,7 +251,8 @@ pub export fn tacacs_authen_start( sessions[idx].port_len = port_len; sessions[idx].authen_action = @enumFromInt(action); sessions[idx].authen_type = @enumFromInt(authen_type); - sessions[idx].last_authen_status = .pass; // Default to pass for simple auth + // No credential verifier is connected: session start is not authentication. + sessions[idx].last_authen_status = .fail; sessions[idx].authen_rounds = 1; sessions[idx].state = .authenticating; return 0; @@ -273,8 +274,8 @@ pub export fn tacacs_authen_continue( if (data_len > MAX_DATA_LEN) return @intFromEnum(AuthenStatus.authen_error); sessions[idx].authen_rounds += 1; - // Simulate: after continue, authentication passes - sessions[idx].last_authen_status = .pass; + // Continuation bytes are not verified by this model; remain failed closed. + sessions[idx].last_authen_status = .fail; return @intFromEnum(sessions[idx].last_authen_status); } @@ -305,6 +306,10 @@ pub export fn tacacs_author_request( if (sessions[idx].state != .authenticating) return @intFromEnum(AuthorStatus.author_error); if (user_len == 0 or user_len > MAX_USER_LEN) return @intFromEnum(AuthorStatus.author_error); if (service_len == 0 or service_len > MAX_NAME_LEN) return @intFromEnum(AuthorStatus.author_error); + if (sessions[idx].last_authen_status != .pass) { + sessions[idx].last_author_status = .author_fail; + return @intFromEnum(AuthorStatus.author_fail); + } sessions[idx].last_author_status = .pass_add; sessions[idx].state = .authorizing; diff --git a/protocols/proven-tacacs/ffi/zig/test/integration_test.zig b/protocols/proven-tacacs/ffi/zig/test/integration_test.zig index d81a1385..a5f5e19a 100644 --- a/protocols/proven-tacacs/ffi/zig/test/integration_test.zig +++ b/protocols/proven-tacacs/ffi/zig/test/integration_test.zig @@ -143,7 +143,7 @@ test "authen_start rejects invalid action" { )); } -test "authen_continue returns status" { +test "authen_continue never accepts unverified credentials" { const secret = "secret"; const slot = tacacs.tacacs_create(secret.ptr, secret.len); defer tacacs.tacacs_destroy(slot); @@ -152,9 +152,9 @@ test "authen_continue returns status" { const port = "tty0"; _ = tacacs.tacacs_authen_start(slot, 0, 0, user.ptr, user.len, port.ptr, port.len); - const data = "password123"; + const data = "credential-bytes"; const status = tacacs.tacacs_authen_continue(slot, data.ptr, data.len); - try std.testing.expectEqual(@as(u8, 0), status); // pass + try std.testing.expectEqual(@as(u8, 1), status); // fail/unverified } test "authen_status returns last status" { @@ -165,14 +165,14 @@ test "authen_status returns last status" { const user = "admin"; const port = ""; _ = tacacs.tacacs_authen_start(slot, 0, 0, user.ptr, user.len, port.ptr, port.len); - try std.testing.expectEqual(@as(u8, 0), tacacs.tacacs_authen_status(slot)); // pass + try std.testing.expectEqual(@as(u8, 1), tacacs.tacacs_authen_status(slot)); // fail/unverified } // ========================================================================= // Authorization // ========================================================================= -test "author_request transitions Authenticating -> Authorizing" { +test "author_request fails closed without successful authentication" { const secret = "secret"; const slot = tacacs.tacacs_create(secret.ptr, secret.len); defer tacacs.tacacs_destroy(slot); @@ -183,8 +183,8 @@ test "author_request transitions Authenticating -> Authorizing" { const service = "shell"; const status = tacacs.tacacs_author_request(slot, user.ptr, user.len, service.ptr, service.len); - try std.testing.expectEqual(@as(u8, 0), status); // pass_add - try std.testing.expectEqual(@as(u8, 2), tacacs.tacacs_state(slot)); // Authorizing + try std.testing.expectEqual(@as(u8, 2), status); // author_fail + try std.testing.expectEqual(@as(u8, 1), tacacs.tacacs_state(slot)); // remains Authenticating } test "author_request rejects from Idle" { @@ -202,7 +202,7 @@ test "author_request rejects from Idle" { // Accounting // ========================================================================= -test "acct_record transitions Authorizing -> Active" { +test "accounting fails closed without an authorized session" { const secret = "secret"; const slot = tacacs.tacacs_create(secret.ptr, secret.len); defer tacacs.tacacs_destroy(slot); @@ -214,8 +214,8 @@ test "acct_record transitions Authorizing -> Active" { _ = tacacs.tacacs_author_request(slot, user.ptr, user.len, service.ptr, service.len); const status = tacacs.tacacs_acct_record(slot, 0, user.ptr, user.len); // start - try std.testing.expectEqual(@as(u8, 0), status); // acct_success - try std.testing.expectEqual(@as(u8, 3), tacacs.tacacs_state(slot)); // Active + try std.testing.expectEqual(@as(u8, 1), status); // acct_error + try std.testing.expectEqual(@as(u8, 1), tacacs.tacacs_state(slot)); // remains Authenticating } test "acct_record rejects invalid flag" { @@ -237,7 +237,7 @@ test "acct_record rejects invalid flag" { // Disconnect / Cleanup // ========================================================================= -test "disconnect transitions Active -> Closing" { +test "disconnect transitions an unauthenticated session -> Closing" { const secret = "secret"; const slot = tacacs.tacacs_create(secret.ptr, secret.len); defer tacacs.tacacs_destroy(slot); diff --git a/protocols/proven-tacacs/src/TACACSABI/Foreign.idr b/protocols/proven-tacacs/src/TACACSABI/Foreign.idr index 5e379a88..2cde29c8 100644 --- a/protocols/proven-tacacs/src/TACACSABI/Foreign.idr +++ b/protocols/proven-tacacs/src/TACACSABI/Foreign.idr @@ -3,15 +3,10 @@ -- -- TACACSABI.Foreign: Foreign function declarations for the C bridge. -- --- Declares the opaque handle type and documents the complete FFI contract --- that the Zig implementation (ffi/zig/src/tacacs.zig) must provide. --- --- The Zig FFI manages: --- - 64-slot mutex-protected session pool --- - Authentication start/continue/reply per session --- - Authorization request/reply per session --- - Accounting start/stop/watchdog per session --- - Session state machine (Idle -> Authenticating -> Authorizing -> Active -> Closing) +-- Declares the opaque handle type and documents the in-memory session model. +-- The Zig FFI has no credential verifier or accounting backend: authentication +-- continuation returns failure, authorization cannot succeed, and accounting +-- is unavailable. Session tags model lifecycle only. -- -- All functions use C calling convention and communicate state via -- Bits8 tags matching TACACSABI.Types exactly. @@ -53,8 +48,8 @@ abiVersion = 1 -- +-----------------------------+-------------------------------------------+ -- | tacacs_create | (secret_ptr: ptr, secret_len: u32) | -- | | -> c_int (slot) | --- | | Creates session with shared secret. | --- | | Returns -1 on failure. | +-- | | Stores session metadata/secret bytes only; | +-- | | no credential verifier is connected. | -- +-----------------------------+-------------------------------------------+ -- | tacacs_destroy | (slot: c_int) -> void | -- | | Releases a session slot. | @@ -66,12 +61,13 @@ abiVersion = 1 -- | | user_ptr: ptr, user_len: u32, | -- | | port_ptr: ptr, port_len: u32) | -- | | -> u8 (0=ok, 1=rejected) | --- | | Starts authentication. | --- | | Transitions Idle -> Authenticating. | +-- | | Starts a modeled conversation only; this | +-- | | does not authenticate the user. | -- +-----------------------------+-------------------------------------------+ -- | tacacs_authen_continue | (slot: c_int, data_ptr: ptr, | -- | | data_len: u32) -> u8 (AuthenStatus tag) | --- | | Continues multi-step authentication. | +-- | | Always returns Fail; continuation bytes | +-- | | are not checked by a credential verifier. | -- +-----------------------------+-------------------------------------------+ -- | tacacs_authen_status | (slot: c_int) -> u8 (AuthenStatus tag) | -- | | Returns last authentication status. | @@ -79,16 +75,16 @@ abiVersion = 1 -- | tacacs_author_request | (slot: c_int, user_ptr: ptr, | -- | | user_len: u32, service_ptr: ptr, | -- | | service_len: u32) -> u8 (AuthorStatus) | --- | | Requests authorization. | --- | | Transitions Authenticating -> Authorizing.| +-- | | Always returns AuthorFail without a | +-- | | successful authentication. | -- +-----------------------------+-------------------------------------------+ -- | tacacs_author_status | (slot: c_int) -> u8 (AuthorStatus tag) | -- | | Returns last authorization status. | -- +-----------------------------+-------------------------------------------+ -- | tacacs_acct_record | (slot: c_int, flag: u8, user_ptr: ptr, | -- | | user_len: u32) -> u8 (AcctStatus tag) | --- | | Sends an accounting record. | --- | | Transitions Authorizing -> Active. | +-- | | Unavailable: no authorization or | +-- | | accounting backend is connected. | -- +-----------------------------+-------------------------------------------+ -- | tacacs_acct_status | (slot: c_int) -> u8 (AcctStatus tag) | -- | | Returns last accounting status. | diff --git a/protocols/proven-triplestore/src/Main.idr b/protocols/proven-triplestore/src/Main.idr index 2c72ee67..763e7888 100644 --- a/protocols/proven-triplestore/src/Main.idr +++ b/protocols/proven-triplestore/src/Main.idr @@ -9,7 +9,6 @@ import Triplestore %default total ||| Print server name, port, and enumerate all type constructors. -partial main : IO () main = do putStrLn "==========================================" diff --git a/protocols/proven-virt/src/Main.idr b/protocols/proven-virt/src/Main.idr index 2b98f8bd..4d01455a 100644 --- a/protocols/proven-virt/src/Main.idr +++ b/protocols/proven-virt/src/Main.idr @@ -9,7 +9,6 @@ import Virt %default total ||| Print server name, ports, and enumerate all type constructors. -partial main : IO () main = do putStrLn "==========================================" diff --git a/protocols/proven-zerotrust/ffi/zig/src/zerotrust.zig b/protocols/proven-zerotrust/ffi/zig/src/zerotrust.zig index 5a1cf996..93fe713a 100644 --- a/protocols/proven-zerotrust/ffi/zig/src/zerotrust.zig +++ b/protocols/proven-zerotrust/ffi/zig/src/zerotrust.zig @@ -3,7 +3,8 @@ // // zerotrust.zig -- Zig FFI implementation of proven-zerotrust. // -// Implements verified Zero Trust access evaluation pipeline with: +// Implements a Zero Trust policy state machine. Identity and device evidence +// backends are not present; caller-supplied scores cannot grant access. // - Slot-based session management (up to 64 concurrent) // - Evaluation phase state machine matching Idris2 Transitions.idr // - Identity confidence tracking (Unverified -> ContinuousAuth) @@ -278,9 +279,10 @@ pub export fn zt_access_decision(slot: c_int) callconv(.c) u8 { // -- Evaluation pipeline transitions ------------------------------------------ -/// Verify identity with given confidence level. -/// RequestReceived -> IdentityVerified (confidence > 0) or AccessDenied (confidence == 0). -/// Returns 0=ok, 1=rejected. +/// Reject caller-supplied identity confidence: no authentication backend or +/// verifiable identity evidence is available. Validly tagged claims move the +/// session to terminal AccessDenied without storing the claimed confidence. +/// Returns 0=verified operation, 1=rejected/unavailable. pub export fn zt_verify_identity(slot: c_int, confidence: u8) callconv(.c) u8 { mutex.lock(); defer mutex.unlock(); @@ -288,15 +290,10 @@ pub export fn zt_verify_identity(slot: c_int, confidence: u8) callconv(.c) u8 { if (contexts[idx].phase != .request_received) return 1; if (confidence > 4) return 1; - contexts[idx].identity_confidence = confidence; - if (confidence == 0) { - // Unverified -> AccessDenied (DenyFromRequest) - contexts[idx].phase = .access_denied; - contexts[idx].access_decision = 1; // Deny - } else { - contexts[idx].phase = .identity_verified; - } - return 0; + contexts[idx].identity_confidence = 0; // Never trust an unverified caller score. + contexts[idx].access_decision = 1; // Deny. + contexts[idx].phase = .access_denied; + return 1; } /// Check device with given trust score. diff --git a/protocols/proven-zerotrust/ffi/zig/test/zerotrust_test.zig b/protocols/proven-zerotrust/ffi/zig/test/zerotrust_test.zig index 050da7c1..13a37aa7 100644 --- a/protocols/proven-zerotrust/ffi/zig/test/zerotrust_test.zig +++ b/protocols/proven-zerotrust/ffi/zig/test/zerotrust_test.zig @@ -123,67 +123,58 @@ test "destroy is safe with invalid slot" { // Full evaluation pipeline: happy path to AccessGranted // ========================================================================= -test "full pipeline: RequestReceived -> IdentityVerified -> DeviceChecked -> PolicyEvaluated -> AccessGranted" { +test "caller-supplied identity and device scores cannot grant access" { const slot = zt.zt_create(2); // LeastPrivilege defer zt.zt_destroy(slot); - // Add signals for good trust score before evaluation - _ = zt.zt_add_signal(slot, 0, 800); // Location: 800 - _ = zt.zt_add_signal(slot, 1, 900); // Time: 900 - _ = zt.zt_add_signal(slot, 2, 700); // Device: 700 - - // Verify identity with MFA - try std.testing.expectEqual(@as(u8, 0), zt.zt_verify_identity(slot, 2)); - try std.testing.expectEqual(@as(u8, 1), zt.zt_phase(slot)); // IdentityVerified - try std.testing.expectEqual(@as(u8, 2), zt.zt_identity_confidence(slot)); // MFAVerified - - // Check device as Managed - try std.testing.expectEqual(@as(u8, 0), zt.zt_check_device(slot, 3)); - try std.testing.expectEqual(@as(u8, 2), zt.zt_phase(slot)); // DeviceChecked - try std.testing.expectEqual(@as(u8, 3), zt.zt_device_trust(slot)); // DeviceManaged + _ = zt.zt_add_signal(slot, 0, 800); + _ = zt.zt_add_signal(slot, 1, 900); + _ = zt.zt_add_signal(slot, 2, 700); - // Evaluate policy - try std.testing.expectEqual(@as(u8, 0), zt.zt_evaluate_policy(slot)); - try std.testing.expectEqual(@as(u8, 3), zt.zt_phase(slot)); // PolicyEvaluated - try std.testing.expectEqual(@as(u8, 0), zt.zt_access_decision(slot)); // Allow + try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 2)); // no verifier + try std.testing.expectEqual(@as(u8, 5), zt.zt_phase(slot)); // AccessDenied + try std.testing.expectEqual(@as(u8, 0), zt.zt_identity_confidence(slot)); // unverified + try std.testing.expectEqual(@as(u8, 1), zt.zt_access_decision(slot)); // Deny - // Grant access - try std.testing.expectEqual(@as(u8, 0), zt.zt_grant_access(slot)); - try std.testing.expectEqual(@as(u8, 4), zt.zt_phase(slot)); // AccessGranted + try std.testing.expectEqual(@as(u8, 1), zt.zt_check_device(slot, 3)); // no attestation + try std.testing.expectEqual(@as(u8, 1), zt.zt_evaluate_policy(slot)); + try std.testing.expectEqual(@as(u8, 1), zt.zt_grant_access(slot)); + try std.testing.expectEqual(@as(u8, 5), zt.zt_phase(slot)); // remains denied } // ========================================================================= // Early denial paths // ========================================================================= -test "early denial: identity verification fails (Unverified)" { +test "identity verification fails closed without an authentication backend" { const slot = zt.zt_create(0); defer zt.zt_destroy(slot); - try std.testing.expectEqual(@as(u8, 0), zt.zt_verify_identity(slot, 0)); // Unverified + try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 0)); try std.testing.expectEqual(@as(u8, 5), zt.zt_phase(slot)); // AccessDenied + try std.testing.expectEqual(@as(u8, 0), zt.zt_identity_confidence(slot)); + try std.testing.expectEqual(@as(u8, 1), zt.zt_access_decision(slot)); // Deny } -test "early denial: device check fails (DeviceUnknown)" { +test "caller-supplied device score cannot bypass missing identity verification" { const slot = zt.zt_create(0); defer zt.zt_destroy(slot); - _ = zt.zt_verify_identity(slot, 2); // MFA - try std.testing.expectEqual(@as(u8, 0), zt.zt_check_device(slot, 0)); // DeviceUnknown + try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 2)); + try std.testing.expectEqual(@as(u8, 1), zt.zt_check_device(slot, 0)); // no attestation try std.testing.expectEqual(@as(u8, 5), zt.zt_phase(slot)); // AccessDenied } -test "policy evaluation denies with low trust score" { - const slot = zt.zt_create(1); // NeverTrust (requires Full) +test "policy evaluation cannot run without verified identity evidence" { + const slot = zt.zt_create(1); // NeverTrust defer zt.zt_destroy(slot); - // No signals added -- trust score will be 0 - _ = zt.zt_verify_identity(slot, 2); - _ = zt.zt_check_device(slot, 2); - _ = zt.zt_evaluate_policy(slot); + try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 2)); + try std.testing.expectEqual(@as(u8, 1), zt.zt_check_device(slot, 2)); + try std.testing.expectEqual(@as(u8, 1), zt.zt_evaluate_policy(slot)); try std.testing.expectEqual(@as(u8, 1), zt.zt_access_decision(slot)); // Deny - _ = zt.zt_grant_access(slot); + try std.testing.expectEqual(@as(u8, 1), zt.zt_grant_access(slot)); try std.testing.expectEqual(@as(u8, 5), zt.zt_phase(slot)); // AccessDenied } @@ -297,21 +288,21 @@ test "cannot skip to AccessGranted (RequestReceived -> AccessGranted)" { try std.testing.expectEqual(@as(u8, 1), zt.zt_grant_access(slot)); // RequestReceived } -test "cannot grant from IdentityVerified (must check device first)" { +test "cannot grant after identity verification is unavailable" { const slot = zt.zt_create(0); defer zt.zt_destroy(slot); - _ = zt.zt_verify_identity(slot, 2); - try std.testing.expectEqual(@as(u8, 1), zt.zt_grant_access(slot)); // IdentityVerified + try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 2)); + try std.testing.expectEqual(@as(u8, 1), zt.zt_grant_access(slot)); // AccessDenied } -test "cannot grant from DeviceChecked (must evaluate policy first)" { +test "cannot grant without identity verification or device attestation" { const slot = zt.zt_create(0); defer zt.zt_destroy(slot); - _ = zt.zt_verify_identity(slot, 2); - _ = zt.zt_check_device(slot, 2); - try std.testing.expectEqual(@as(u8, 1), zt.zt_grant_access(slot)); // DeviceChecked + try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 2)); + try std.testing.expectEqual(@as(u8, 1), zt.zt_check_device(slot, 2)); + try std.testing.expectEqual(@as(u8, 1), zt.zt_grant_access(slot)); // never reaches evaluation } test "verify_identity rejects invalid confidence tag" { @@ -320,30 +311,26 @@ test "verify_identity rejects invalid confidence tag" { try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 99)); } -test "check_device rejects invalid trust tag" { +test "device score is rejected after identity evidence is unavailable" { const slot = zt.zt_create(0); defer zt.zt_destroy(slot); - _ = zt.zt_verify_identity(slot, 2); + try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 2)); try std.testing.expectEqual(@as(u8, 1), zt.zt_check_device(slot, 99)); + try std.testing.expectEqual(@as(u8, 5), zt.zt_phase(slot)); } // ========================================================================= // Terminal state enforcement // ========================================================================= -test "AccessGranted is terminal: no further transitions" { +test "AccessGranted is unreachable without an evidence backend" { const slot = zt.zt_create(2); defer zt.zt_destroy(slot); _ = zt.zt_add_signal(slot, 0, 800); - _ = zt.zt_verify_identity(slot, 3); - _ = zt.zt_check_device(slot, 3); - _ = zt.zt_evaluate_policy(slot); - _ = zt.zt_grant_access(slot); - - // All transitions should be rejected - try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 2)); - try std.testing.expectEqual(@as(u8, 1), zt.zt_check_device(slot, 2)); + try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 3)); + try std.testing.expectEqual(@as(u8, 5), zt.zt_phase(slot)); // denied, never granted + try std.testing.expectEqual(@as(u8, 1), zt.zt_check_device(slot, 3)); try std.testing.expectEqual(@as(u8, 1), zt.zt_evaluate_policy(slot)); try std.testing.expectEqual(@as(u8, 1), zt.zt_grant_access(slot)); } @@ -352,7 +339,7 @@ test "AccessDenied is terminal: no further transitions" { const slot = zt.zt_create(0); defer zt.zt_destroy(slot); - _ = zt.zt_verify_identity(slot, 0); // -> AccessDenied + try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 0)); // -> AccessDenied try std.testing.expectEqual(@as(u8, 1), zt.zt_verify_identity(slot, 2)); try std.testing.expectEqual(@as(u8, 1), zt.zt_check_device(slot, 2)); diff --git a/protocols/proven-zerotrust/proven-zerotrust.ipkg b/protocols/proven-zerotrust/proven-zerotrust.ipkg index c8ee2b9c..cf6ebe6e 100644 --- a/protocols/proven-zerotrust/proven-zerotrust.ipkg +++ b/protocols/proven-zerotrust/proven-zerotrust.ipkg @@ -5,7 +5,7 @@ package proven-zerotrust version = "0.1.0" authors = "Jonathan D.A. Jewell" license = "MPL-2.0" -brief = "Zero Trust continuous authentication and policy enforcement" +brief = "Zero Trust policy state-machine model; no identity or device verifier, denies by default" sourcedir = "src" main = Main diff --git a/protocols/proven-zerotrust/src/Main.idr b/protocols/proven-zerotrust/src/Main.idr index 9358f2ee..3e9570aa 100644 --- a/protocols/proven-zerotrust/src/Main.idr +++ b/protocols/proven-zerotrust/src/Main.idr @@ -29,7 +29,7 @@ allSessionStates = [Unauthenticated, PartialAuth, Authenticated, Elevated, Locke main : IO () main = do - putStrLn "proven-zerotrust : Zero Trust authentication server" + putStrLn "proven-zerotrust : Zero Trust policy state model (denies without identity/device evidence)" putStrLn $ " Max session duration: " ++ show maxSessionDuration ++ " seconds" putStrLn $ " Reauth interval: " ++ show reauthInterval ++ " seconds" putStrLn $ " AuthFactors: " ++ show allAuthFactors diff --git a/protocols/proven-zerotrust/src/ZeroTrustABI/Foreign.idr b/protocols/proven-zerotrust/src/ZeroTrustABI/Foreign.idr index 6623c54f..3471e71d 100644 --- a/protocols/proven-zerotrust/src/ZeroTrustABI/Foreign.idr +++ b/protocols/proven-zerotrust/src/ZeroTrustABI/Foreign.idr @@ -6,12 +6,9 @@ -- Declares the opaque handle type and documents the complete FFI contract -- that the Zig implementation (ffi/zig/src/zerotrust.zig) must provide. -- --- The Zig FFI manages: --- - 64-slot mutex-protected session pool --- - Policy engine with configurable policy types --- - Trust score calculation from context signals --- - Signal aggregation across multiple context dimensions --- - Access evaluation pipeline (GADT-aligned state machine) +-- The Zig FFI models the policy state machine and caller-supplied signal +-- aggregation only. No identity verifier or device attestation backend exists; +-- claimed confidence/trust scores are not evidence and access is denied. -- -- All functions use C calling convention and communicate state via -- Bits8 tags matching ZeroTrustABI.Layout exactly. @@ -76,37 +73,28 @@ abiVersion = 1 -- | | after PolicyEvaluated/Granted/Denied). | -- +-----------------------------+---------------------------------------------+ -- | zt_verify_identity | (slot: c_int, confidence: u8) -> u8 | --- | | Verify identity with given confidence level.| --- | | Transitions: RequestReceived -> | --- | | IdentityVerified (if confidence > 0) | --- | | or AccessDenied (if confidence == 0). | --- | | Returns 0=ok, 1=rejected. | +-- | | Rejects caller-supplied confidence because | +-- | | no authentication backend exists. Valid tags| +-- | | move RequestReceived -> AccessDenied; | +-- | | confidence is not stored; returns 1. | -- +-----------------------------+---------------------------------------------+ -- | zt_check_device | (slot: c_int, trust: u8) -> u8 | --- | | Check device with given trust score. | --- | | Transitions: IdentityVerified -> | --- | | DeviceChecked (if trust > 0) | --- | | or AccessDenied (if trust == 0). | --- | | Returns 0=ok, 1=rejected. | +-- | | Always rejects: no identity evidence or | +-- | | device attestation backend is connected. | +-- | | Caller-supplied scores are never stored. | -- +-----------------------------+---------------------------------------------+ -- | zt_evaluate_policy | (slot: c_int) -> u8 | --- | | Evaluate all policies against current | --- | | context signals, identity, and device trust.| --- | | Transitions: DeviceChecked -> | --- | | PolicyEvaluated. | --- | | Returns 0=ok, 1=rejected. | +-- | | Unreachable: no verified identity or | +-- | | device state can be established. | +-- | | Caller-supplied signals do not grant access.| -- +-----------------------------+---------------------------------------------+ -- | zt_grant_access | (slot: c_int) -> u8 | --- | | Grant access after policy evaluation. | --- | | Transitions: PolicyEvaluated -> | --- | | AccessGranted (if decision is Allow) | --- | | or AccessDenied (otherwise). | --- | | Returns 0=ok, 1=rejected. | +-- | | Cannot grant in this FFI: no verified | +-- | | identity/device evidence reaches policy. | -- +-----------------------------+---------------------------------------------+ -- | zt_add_signal | (slot: c_int, kind: u8, value: u16) -> u8 | --- | | Add a context signal with a 0-1000 score. | --- | | Can be called at any non-terminal phase. | --- | | Returns 0=ok, 1=rejected. | +-- | | Store caller-supplied signal metadata only; | +-- | | it is not verified evidence for access. | -- +-----------------------------+---------------------------------------------+ -- | zt_signal_count | (slot: c_int) -> u32 | -- | | Returns number of active context signals. | @@ -116,8 +104,8 @@ abiVersion = 1 -- | | Returns 0 if signal not set. | -- +-----------------------------+---------------------------------------------+ -- | zt_trust_score | (slot: c_int) -> u16 | --- | | Compute aggregate trust score from all | --- | | active signals (weighted average, 0-1000). | +-- | | Compute an aggregate of caller-supplied | +-- | | metadata; this is not identity evidence. | -- +-----------------------------+---------------------------------------------+ -- | zt_trust_level | (slot: c_int) -> u8 (TrustLevel tag) | -- | | Returns trust level derived from aggregate | diff --git a/selur-compose.toml b/selur-compose.toml deleted file mode 100644 index c40aecbc..00000000 --- a/selur-compose.toml +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# -# Stapeln selur-compose.toml — Proven Servers -# Idris2 ABI + Zig FFI formally verified server library - -[project] -name = "proven-servers" - -[services.app] -build = { context = ".", dockerfile = "Containerfile" } -restart = "unless-stopped" -networks = ["default"] - -healthcheck = { test = "/usr/local/bin/proven_servers --version || exit 1", interval = "30s", timeout = "5s", start_period = "5s", retries = 3 } - -[services.app.labels] -"org.opencontainers.image.title" = "Proven Servers" -"org.opencontainers.image.description" = "Formally verified server library (Idris2 ABI + Zig FFI)" - -[networks.default] -driver = "bridge" diff --git a/setup.sh b/setup.sh index ea57e560..32bf51ef 100644 --- a/setup.sh +++ b/setup.sh @@ -1,278 +1,26 @@ -#!/bin/sh +#!/usr/bin/env sh # SPDX-License-Identifier: MPL-2.0 -# setup.sh — Universal setup script for proven-servers +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# Detects your shell, platform, and installs prerequisites. -# Then hands off to `just setup` for project-specific configuration. -# -# Usage: -# curl -fsSL https://raw.githubusercontent.com/hyperpolymath/proven-servers/main/setup.sh | sh -# # or after cloning: -# ./setup.sh -# -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# Non-mutating workspace status helper. This is deliberately not an installer: +# it never downloads or executes remote scripts, installs packages, changes +# firewall/SELinux state, writes reports, or deploys anything. set -eu -# ── Colours (safe — uses symbols too per ADJUST contractile) ── -if [ -t 1 ] && command -v tput >/dev/null 2>&1; then - RED=$(tput setaf 1 2>/dev/null || true) - GREEN=$(tput setaf 2 2>/dev/null || true) - YELLOW=$(tput setaf 3 2>/dev/null || true) - CYAN=$(tput setaf 6 2>/dev/null || true) - BOLD=$(tput bold 2>/dev/null || true) - RESET=$(tput sgr0 2>/dev/null || true) -else - RED="" GREEN="" YELLOW="" CYAN="" BOLD="" RESET="" +if ! command -v just >/dev/null 2>&1; then + printf '%s\n' \ + 'ERROR: Just is required to display repository task information.' \ + 'Install Just using your platform-approved package source, then run `just info`.' >&2 + exit 127 fi -ok() { printf " %s[OK]%s %s\n" "$GREEN" "$RESET" "$1"; } -fail() { printf " %s[FAIL]%s %s\n" "$RED" "$RESET" "$1"; } -warn() { printf " %s[WARN]%s %s\n" "$YELLOW" "$RESET" "$1"; } -info() { printf " %s[INFO]%s %s\n" "$CYAN" "$RESET" "$1"; } - -# ── Shell Detection ── -detect_shell() { - # Check the actual running shell, not just $SHELL - CURRENT_SHELL="unknown" - - if [ -n "${BASH_VERSION:-}" ]; then CURRENT_SHELL="bash" - elif [ -n "${ZSH_VERSION:-}" ]; then CURRENT_SHELL="zsh" - elif [ -n "${FISH_VERSION:-}" ]; then CURRENT_SHELL="fish" - elif [ -n "${KSH_VERSION:-}" ]; then CURRENT_SHELL="ksh" - # Check by process name for shells that don't set version vars - elif command -v ps >/dev/null 2>&1; then - SHELL_PROC=$(ps -p $$ -o comm= 2>/dev/null || echo "unknown") - case "$SHELL_PROC" in - *dash*) CURRENT_SHELL="dash" ;; - *tcsh*) CURRENT_SHELL="tcsh" ;; - *csh*) CURRENT_SHELL="csh" ;; - *elvish*) CURRENT_SHELL="elvish" ;; - *nu*) CURRENT_SHELL="nushell" ;; - *oil*|*osh*) CURRENT_SHELL="oil" ;; - *xonsh*) CURRENT_SHELL="xonsh" ;; - *murex*) CURRENT_SHELL="murex" ;; - *ion*) CURRENT_SHELL="ion" ;; - *hilbish*) CURRENT_SHELL="hilbish" ;; - *oh*) CURRENT_SHELL="oh" ;; - *vsh*) CURRENT_SHELL="vsh" ;; - *pwsh*|*powershell*) CURRENT_SHELL="powershell" ;; - esac - fi - - # Fallback: check $SHELL env var - if [ "$CURRENT_SHELL" = "unknown" ] && [ -n "${SHELL:-}" ]; then - case "$SHELL" in - */bash) CURRENT_SHELL="bash" ;; - */zsh) CURRENT_SHELL="zsh" ;; - */fish) CURRENT_SHELL="fish" ;; - */dash) CURRENT_SHELL="dash" ;; - */ksh*) CURRENT_SHELL="ksh" ;; - */tcsh) CURRENT_SHELL="tcsh" ;; - */csh) CURRENT_SHELL="csh" ;; - */vsh) CURRENT_SHELL="vsh" ;; - esac - fi - - printf "%s" "$CURRENT_SHELL" -} - -# ── Platform Detection ── -detect_platform() { - OS="unknown" - DISTRO="unknown" - PKG_MGR="unknown" - ARCH=$(uname -m 2>/dev/null || echo "unknown") - - case "$(uname -s 2>/dev/null)" in - Linux*) - OS="linux" - if [ -f /etc/os-release ]; then - DISTRO=$(. /etc/os-release && echo "$ID") - elif [ -f /etc/redhat-release ]; then - DISTRO="rhel" - elif [ -f /etc/debian_version ]; then - DISTRO="debian" - fi - # Detect package manager - if command -v dnf >/dev/null 2>&1; then PKG_MGR="dnf" - elif command -v apt-get >/dev/null 2>&1; then PKG_MGR="apt" - elif command -v pacman >/dev/null 2>&1; then PKG_MGR="pacman" - elif command -v apk >/dev/null 2>&1; then PKG_MGR="apk" - elif command -v zypper >/dev/null 2>&1; then PKG_MGR="zypper" - elif command -v rpm-ostree >/dev/null 2>&1; then PKG_MGR="rpm-ostree" - elif command -v guix >/dev/null 2>&1; then PKG_MGR="guix" - elif command -v nix >/dev/null 2>&1; then PKG_MGR="nix" - fi - ;; - Darwin*) - OS="macos" - DISTRO="macos" - if command -v brew >/dev/null 2>&1; then PKG_MGR="brew" - elif command -v port >/dev/null 2>&1; then PKG_MGR="macports" - fi - ;; - CYGWIN*|MINGW*|MSYS*) - OS="windows" - DISTRO="msys" - if command -v winget >/dev/null 2>&1; then PKG_MGR="winget" - elif command -v scoop >/dev/null 2>&1; then PKG_MGR="scoop" - elif command -v choco >/dev/null 2>&1; then PKG_MGR="choco" - fi - ;; - FreeBSD*) - OS="freebsd" - DISTRO="freebsd" - PKG_MGR="pkg" - ;; - esac -} - -# ── Install just ── -install_just() { - if command -v just >/dev/null 2>&1; then - ok "just already installed: $(just --version 2>/dev/null | head -1)" - return 0 - fi - - info "Installing just (task runner)..." - - case "$PKG_MGR" in - dnf) sudo dnf install -y just ;; - apt) sudo apt-get install -y just 2>/dev/null || { - # just not in older apt repos — use installer - curl -fsSL https://just.systems/install.sh | bash -s -- --to /usr/local/bin - } ;; - pacman) sudo pacman -S --noconfirm just ;; - apk) sudo apk add just ;; - brew) brew install just ;; - scoop) scoop install just ;; - winget) winget install Casey.Just ;; - rpm-ostree) sudo rpm-ostree install just ;; - guix) guix install just ;; - nix) nix-env -iA nixpkgs.just ;; - *) - info "Using just installer script..." - curl -fsSL https://just.systems/install.sh | bash -s -- --to /usr/local/bin - ;; - esac - - if command -v just >/dev/null 2>&1; then - ok "just installed: $(just --version 2>/dev/null | head -1)" - else - fail "Could not install just. Install manually: https://just.systems/" - return 1 - fi -} - -# ── Main ── -main() { - printf "%s=== proven-servers Setup ===%s\n\n" "$BOLD" "$RESET" - - # Detect environment - SHELL_NAME=$(detect_shell) - detect_platform - - info "Shell: $SHELL_NAME" - info "Platform: $OS ($DISTRO)" - info "Arch: $ARCH" - info "Packages: $PKG_MGR" - printf "\n" - - # Warn about exotic shells - case "$SHELL_NAME" in - vsh) - info "Valence Shell detected — experimental support" - info "Falling back to POSIX sh for setup, vsh for post-setup" - ;; - nushell|elvish|murex|ion|hilbish|oil|xonsh|oh) - info "$SHELL_NAME detected — using POSIX sh for setup" - ;; - esac - - # Step 1: Install just - printf "%sStep 1: Install task runner%s\n" "$BOLD" "$RESET" - install_just || { fail "Cannot proceed without just"; exit 1; } - printf "\n" - - # Step 2: Check if we're in the repo directory - if [ ! -f "Justfile" ] && [ ! -f "justfile" ]; then - warn "Not in a repo directory (no Justfile found)" - info "Clone first: git clone https://github.com/hyperpolymath/proven-servers.git" - info "Then: cd proven-servers && ./setup.sh" - exit 1 - fi - - # Step 3: Run just setup - printf "%sStep 2: Project setup%s\n" "$BOLD" "$RESET" - if just --list 2>/dev/null | grep -q "^setup "; then - just setup - elif just --list 2>/dev/null | grep -q "^setup-dev "; then - just setup-dev - else - warn "No 'setup' recipe in Justfile — running 'just doctor' instead" - just doctor 2>/dev/null || true - fi - printf "\n" - - # Step 4: Post-install security snapshot - printf "%sStep 3: Security snapshot%s\n" "$BOLD" "$RESET" - if command -v firewall-cmd >/dev/null 2>&1; then - if firewall-cmd --state 2>/dev/null | grep -q running; then - ok "Firewall: firewalld active" - else - warn "Firewall: firewalld installed but not running" - info " Enable: sudo systemctl enable --now firewalld" - fi - elif command -v ufw >/dev/null 2>&1; then - if ufw status 2>/dev/null | grep -q "Status: active"; then - ok "Firewall: ufw active" - else - warn "Firewall: ufw installed but not active" - info " Enable: sudo ufw enable" - fi - else - warn "Firewall: none detected" - case "$PKG_MGR" in - dnf|rpm-ostree) info " Install: sudo dnf install firewalld && sudo systemctl enable --now firewalld" ;; - apt) info " Install: sudo apt install ufw && sudo ufw enable" ;; - *) info " Install a firewall for your platform" ;; - esac - fi - - if command -v getenforce >/dev/null 2>&1; then - SE_STATUS=$(getenforce 2>/dev/null || echo "unknown") - case "$SE_STATUS" in - Enforcing) ok "SELinux: Enforcing" ;; - Permissive) warn "SELinux: Permissive (recommend Enforcing: sudo setenforce 1)" ;; - *) warn "SELinux: $SE_STATUS" ;; - esac - fi - - # Write report - REPORT_FILE="INSTALL-SECURITY-REPORT.adoc" - { - printf "// SPDX-License-Identifier: MPL-2.0\n" - printf "= Install Security Report\n" - printf ":date: %s\n\n" "$(date -Iseconds 2>/dev/null || date)" - printf "== Platform\n" - printf "* OS: %s (%s)\n" "$OS" "$DISTRO" - printf "* Arch: %s\n" "$ARCH" - printf "* Package manager: %s\n" "$PKG_MGR" - printf "* Shell: %s\n\n" "$SHELL_NAME" - printf "== Security Status\n" - printf "Run \`just doctor\` for full diagnostic.\n" - } > "$REPORT_FILE" - info "Security report: $REPORT_FILE" - printf "\n" - - # Done - printf "%s=== Setup Complete ===%s\n\n" "${BOLD}${GREEN}" "$RESET" - printf "Next steps:\n" - printf " just doctor — verify everything works\n" - printf " just tour — guided tour of the project\n" - printf " just build — build the project\n" - printf " just help-me — get help if stuck\n" -} +if [ ! -f Justfile ]; then + printf '%s\n' 'ERROR: run this helper from the proven-servers repository root.' >&2 + exit 2 +fi -main "$@" +just info +printf '\n%s\n' \ + 'This helper does not install Idris2 or Zig.' \ + 'See QUICKSTART-DEV.adoc for package-scoped build and test instructions.' diff --git a/stapeln.toml b/stapeln.toml deleted file mode 100644 index 09aadf81..00000000 --- a/stapeln.toml +++ /dev/null @@ -1,91 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# stapeln.toml — Layer-based container build for proven-servers -# -# stapeln builds containers as composable layers (German: "to stack"). -# Each layer is independently cacheable, verifiable, and signable. - -[metadata] -name = "proven-servers" -version = "0.1.0" -description = "proven-servers container service" -author = "Jonathan D.A. Jewell " -license = "MPL-2.0" -registry = "ghcr.io/hyperpolymath" - -[build] -containerfile = "Containerfile" -context = "." -runtime = "podman" - -# ── Layer Definitions ────────────────────────────────────────── - -[layers.base] -description = "Chainguard Wolfi minimal base" -from = "cgr.dev/chainguard/wolfi-base:latest" -cache = true -verify = true - -[layers.zig-toolchain] -description = "Zig compiler" -extends = "base" -packages = ["zig"] -cache = true - -[layers.build] -description = "proven-servers Zig compilation" -extends = "zig-toolchain" -commands = ["zig build -Doptimize=ReleaseSafe"] -artifacts = [ - { src = "zig-out/bin/proven-servers", dst = "/app/proven-servers" }, -] - -[layers.runtime] -description = "Minimal runtime" -from = "cgr.dev/chainguard/wolfi-base:latest" -packages = ["ca-certificates", "curl"] -copy-from = [ - { layer = "build", src = "/app/", dst = "/app/" }, -] -entrypoint = ["["/usr/local/bin/proven_servers"]"] -user = "nonroot" -env = { PATH = "/root/.pack/bin:${PATH}" } - -# ── Security ─────────────────────────────────────────────────── - -[security] -non-root = true -read-only-root = false -no-new-privileges = true -cap-drop = ["ALL"] -seccomp-profile = "default" - -[security.signing] -algorithm = "ML-DSA-87" -provider = "cerro-torre" - -[security.sbom] -format = "spdx-json" -output = "sbom.spdx.json" -include-deps = true - -# ── Verification ─────────────────────────────────────────────── - -[verify] -vordr = true -svalinn = true -scan-on-build = true -fail-on = ["critical", "high"] - -# ── Targets ──────────────────────────────────────────────────── - -[targets.development] -layers = ["base", "zig-toolchain", "build"] -env = { LOG_LEVEL = "debug" } - -[targets.production] -layers = ["runtime"] -env = { LOG_LEVEL = "info" } - -[targets.test] -layers = ["base", "zig-toolchain", "build"] -env = { LOG_LEVEL = "debug" } diff --git a/tests/.gitkeep b/tests/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/aspect/security_test.sh b/tests/aspect/security_test.sh index 7405015c..91ca2e01 100644 --- a/tests/aspect/security_test.sh +++ b/tests/aspect/security_test.sh @@ -2,12 +2,11 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# proven-servers — Security Aspect Test Suite +# proven-servers — Static Security-Heuristic Smoke Checks # -# Tests security-specific properties of the proven-servers protocol -# implementations. These are aspect tests — they cut across all protocols -# and verify that the security invariants established in the Idris2 ABI -# specifications are preserved in the Zig FFI implementations. +# This script uses grep/source-shape checks over a small sample. It does not +# execute the FFI or establish security properties, and it is not a security +# certification or proof that Idris specifications and Zig code conform. # # Security aspects covered # ──────────────────────── @@ -75,8 +74,17 @@ pass() { green " PASS: $1"; PASS=$((PASS + 1)); } fail_test() { red " FAIL: $1"; FAIL=$((FAIL + 1)); } skip_test() { yellow " SKIP: $1 ($2)"; SKIP=$((SKIP + 1)); } +exported_function_body() { + local function_name="$1" source_file="$2" + awk -v fn="$function_name" ' + !capture && index($0, "pub export fn " fn "(") > 0 { capture=1 } + capture { print } + capture && /^}/ { exit } + ' "$source_file" +} + echo "═══════════════════════════════════════════════════════════════" -echo " proven-servers — Security Aspect Tests" +echo " proven-servers — Static Security-Heuristic Smoke Checks (not certification)" echo "═══════════════════════════════════════════════════════════════" echo "" @@ -93,7 +101,7 @@ echo "" # MQTT: Idle(0) → Subscribed(2) — skips Connected authentication # DNS: Idle(0) → ResponseBuilding(3) — skips query parsing # ───────────────────────────────────────────────────────────────────────────── -bold "SA1 — State machine cannot skip handshake states" +bold "SA1 — Selected transition bypass source checks" # Format: "proto bypass_from bypass_to description" declare -a SA1_CASES=( @@ -107,11 +115,7 @@ declare -a SA1_CASES=( ) for entry in "${SA1_CASES[@]}"; do - # shellcheck disable=SC2086 - proto=$(echo "$entry" | awk '{print $1}') - bypass_from=$(echo "$entry" | awk '{print $2}') - bypass_to=$(echo "$entry" | awk '{print $3}') - description=$(echo "$entry" | cut -d' ' -f4-) + read -r proto bypass_from bypass_to description <<< "$entry" SRC_FILE="protocols/proven-${proto}/ffi/zig/src/${proto}.zig" if [ ! -f "$SRC_FILE" ]; then @@ -119,25 +123,28 @@ for entry in "${SA1_CASES[@]}"; do continue fi - # The bypass transition must NOT appear as an accepted edge (return 1). + transition_body="$(exported_function_body "${proto}_can_transition" "$SRC_FILE")" + if [ -z "$transition_body" ]; then + skip_test "SA1 proven-${proto}: ${description}" "transition function not found" + continue + fi BYPASS_PATTERN="from == ${bypass_from} and to == ${bypass_to}" - if grep "$BYPASS_PATTERN" "$SRC_FILE" 2>/dev/null | grep -q "return 1"; then - fail_test "SA1 proven-${proto}: BYPASS ACCEPTED — ${description}" + if grep "$BYPASS_PATTERN" <<<"$transition_body" | grep -q "return 1"; then + fail_test "SA1 proven-${proto}: selected edge appears accepted — ${description}" else - pass "SA1 proven-${proto}: bypass correctly rejected — ${description}" + pass "SA1 proven-${proto}: selected edge text not found in accepted branch — ${description}" fi done echo "" # ───────────────────────────────────────────────────────────────────────────── -# SA2 — Buffer overflow prevention: explicit length bounds before memory ops +# SA2 — Coarse source scan for selected length-comparison patterns # -# Security invariant: any function that receives a pointer + length pair must -# validate the length against an upper bound BEFORE performing any array -# access or memcpy-equivalent. We verify that every protocol's create/parse -# function contains an explicit length guard. +# This only looks for at least one textual comparison in each selected source +# file. It does not associate the comparison with every pointer or memory +# operation, and it does not prove ordering, bounds safety, or runtime behavior. # ───────────────────────────────────────────────────────────────────────────── -bold "SA2 — Buffer overflow prevention: length bounds enforced" +bold "SA2 — Presence of selected length-guard source patterns" declare -a SA2_PROTOCOLS=( "amqp" "dns" "mqtt" "smtp" "ftp" "cache" "ca" @@ -155,7 +162,7 @@ for proto in "${SA2_PROTOCOLS[@]}"; do # Valid patterns: `> MAX_*`, `>= MAX_*`, `== 0`, or a named max constant check. # These are the guard patterns the Zig FFI code must contain. if grep -qE "([lg][te]|==)[[:space:]]*(MAX_[A-Z_]+|0)" "$SRC_FILE"; then - pass "SA2 proven-${proto}: explicit length/size guard present" + pass "SA2 proven-${proto}: at least one length-guard pattern is present" else fail_test "SA2 proven-${proto}: NO explicit length guard found — potential overflow" fi @@ -173,7 +180,7 @@ echo "" # We verify that authenticated/operating states are only reachable through # the intermediate states by checking the transition table structure. # ───────────────────────────────────────────────────────────────────────────── -bold "SA3 — Authentication spoofing prevention (handshake ordering)" +bold "SA3 — Selected handshake-edge source checks" # Format: "proto auth_state must_come_from description" declare -a SA3_CASES=( @@ -185,10 +192,7 @@ declare -a SA3_CASES=( ) for entry in "${SA3_CASES[@]}"; do - proto=$(echo "$entry" | awk '{print $1}') - auth_state=$(echo "$entry" | awk '{print $2}') - must_come_from=$(echo "$entry" | awk '{print $3}') - description=$(echo "$entry" | cut -d' ' -f4-) + read -r proto auth_state must_come_from description <<< "$entry" SRC_FILE="protocols/proven-${proto}/ffi/zig/src/${proto}.zig" if [ ! -f "$SRC_FILE" ]; then @@ -197,21 +201,24 @@ for entry in "${SA3_CASES[@]}"; do fi # The required transition (must_come_from → auth_state) must be accepted. + transition_body="$(exported_function_body "${proto}_can_transition" "$SRC_FILE")" + if [ -z "$transition_body" ]; then + skip_test "SA3 proven-${proto}: ${description}" "transition function not found" + continue + fi REQUIRED="from == ${must_come_from} and to == ${auth_state}" - if grep -q "$REQUIRED" "$SRC_FILE" && grep "$REQUIRED" "$SRC_FILE" | grep -q "return 1"; then - pass "SA3 proven-${proto}: ${description}" + if grep -q "$REQUIRED" <<<"$transition_body" && grep "$REQUIRED" <<<"$transition_body" | grep -q "return 1"; then + pass "SA3 proven-${proto}: selected required edge appears in source — ${description}" else - fail_test "SA3 proven-${proto}: required auth transition NOT present — ${description}" + fail_test "SA3 proven-${proto}: selected required edge not found — ${description}" fi - # Transitions to auth_state from state 0 (if different from must_come_from) - # must NOT be accepted, unless must_come_from == 0. if [ "$must_come_from" != "0" ]; then BYPASS="from == 0 and to == ${auth_state}" - if grep "$BYPASS" "$SRC_FILE" 2>/dev/null | grep -q "return 1"; then - fail_test "SA3 proven-${proto}: auth state ${auth_state} reachable directly from Idle — spoofing possible" + if grep "$BYPASS" <<<"$transition_body" | grep -q "return 1"; then + fail_test "SA3 proven-${proto}: selected bypass appears accepted — ${description}" else - pass "SA3 proven-${proto}: auth state ${auth_state} NOT reachable directly from Idle" + pass "SA3 proven-${proto}: selected bypass not accepted by source pattern" fi fi done @@ -226,7 +233,7 @@ echo "" # slot >= MAX_SESSIONS or the validSlot() wrapper). # 2. The validSlot (or equivalent) function short-circuits before indexing. # ───────────────────────────────────────────────────────────────────────────── -bold "SA4 — Invalid slot safety (out-of-bounds access handled)" +bold "SA4 — Slot-validator source patterns (not per-call verification)" declare -a SA4_PROTOCOLS=( "amqp" "dns" "mqtt" "smtp" "ftp" "cache" "ca" @@ -254,7 +261,7 @@ for proto in "${SA4_PROTOCOLS[@]}"; do fi if [ "$HAS_SLOT_GUARD" -eq 1 ]; then - pass "SA4 proven-${proto}: slot bounds guard present" + pass "SA4 proven-${proto}: a slot-guard pattern is present" else fail_test "SA4 proven-${proto}: NO slot bounds guard — invalid indices may be dereferenced" fi @@ -268,7 +275,7 @@ echo "" # is therefore exploitable as a denial-of-service vector. All error paths # in FFI production code must return error codes, not panic. # ───────────────────────────────────────────────────────────────────────────── -bold "SA5 — No @panic in FFI production code (DoS prevention)" +bold "SA5 — Text scan for @panic in selected FFI source trees" PANIC_IN_PROD=0 PROD_FILES_CHECKED=0 @@ -298,7 +305,7 @@ echo "" # Idris2 bypass the type checker and can introduce type-unsafe casts. These # patterns in the ABI specification layer undermine the formal guarantees. # ───────────────────────────────────────────────────────────────────────────── -bold "SA6 — No dangerous Idris2 patterns in ABI specs" +bold "SA6 — Text scan for selected Idris escape-hatch identifiers" DANGEROUS_COUNT=0 IDRIS_FILES_CHECKED=0 @@ -309,10 +316,11 @@ for idr in protocols/proven-*/src/**/*.idr \ connectors/proven-*/src/**/*.idr; do [ -f "$idr" ] || continue IDRIS_FILES_CHECKED=$((IDRIS_FILES_CHECKED + 1)) - if grep -qE "believe_me|assert_total|really_believe_me" "$idr"; then - hit=$(grep -nE "believe_me|assert_total|really_believe_me" "$idr" | head -3) - fail_test "SA6 dangerous Idris2 pattern in: $idr - $hit" + hits="$(grep -nE 'believe_me|assert_total|really_believe_me' "$idr" \ + | grep -vE '^[0-9]+:[[:space:]]*(--|\|\|\|)' || true)" + if [ -n "$hits" ]; then + fail_test "SA6 possible active Idris2 escape hatch in: $idr + $(printf '%s\n' "$hits" | head -3)" DANGEROUS_COUNT=$((DANGEROUS_COUNT + 1)) fi done @@ -331,7 +339,7 @@ echo "" # arise when multiple threads access the session pool without synchronisation. # Every protocol that maintains global session state must acquire a mutex. # ───────────────────────────────────────────────────────────────────────────── -bold "SA7 — Mutex protection: global session state is guarded" +bold "SA7 — Mutex-related source patterns in selected modules" declare -a SA7_PROTOCOLS=( "amqp" "dns" "mqtt" "smtp" "cache" "ca" "bfd" @@ -359,7 +367,7 @@ for proto in "${SA7_PROTOCOLS[@]}"; do fi if [ "$HAS_MUTEX" -eq 1 ] && [ "$HAS_LOCK" -eq 1 ] && [ "$HAS_UNLOCK" -eq 1 ]; then - pass "SA7 proven-${proto}: mutex declared, lock acquired, unlock deferred" + pass "SA7 proven-${proto}: mutex/lock/unlock text patterns are present" elif [ "$HAS_MUTEX" -eq 0 ]; then fail_test "SA7 proven-${proto}: NO mutex declaration — TOCTOU vulnerability" elif [ "$HAS_LOCK" -eq 0 ]; then @@ -377,7 +385,7 @@ echo "" # are error-prone and likely to be misused. Every protocol with name-based # lookups or string fields must declare named maximum length constants. # ───────────────────────────────────────────────────────────────────────────── -bold "SA8 — Maximum length constants defined (buffer overflow prevention)" +bold "SA8 — MAX_* constants in selected modules" declare -a SA8_PROTOCOLS=( "amqp" "dns" "mqtt" "smtp" "ftp" "cache" "ca" @@ -395,7 +403,7 @@ for proto in "${SA8_PROTOCOLS[@]}"; do if grep -qE "const MAX_[A-Z_]+(:[[:space:]]*usize)?[[:space:]]*=" "$SRC_FILE"; then # Count the number of MAX_ constants for information. max_count=$(grep -cE "const MAX_[A-Z_]+" "$SRC_FILE" || echo 0) - pass "SA8 proven-${proto}: ${max_count} MAX_* length constant(s) defined" + pass "SA8 proven-${proto}: ${max_count} MAX_* declaration(s) are present" else fail_test "SA8 proven-${proto}: NO MAX_* length constants — unbounded buffers possible" fi @@ -409,7 +417,7 @@ echo "" # on repository clone. We scan for common patterns: hardcoded passwords, # API keys, private key material, and base64-encoded secrets. # ───────────────────────────────────────────────────────────────────────────── -bold "SA9 — No hardcoded credentials or secrets in FFI source" +bold "SA9 — Credential-shaped text scan in FFI source" SECRET_PATTERNS=( "password[[:space:]]*=[[:space:]]*\"" @@ -447,7 +455,7 @@ done if [ "$FILES_CHECKED" -eq 0 ]; then skip_test "SA9 credential scan" "no production FFI source files found" elif [ "$SECRETS_FOUND" -eq 0 ]; then - pass "SA9 no hardcoded credentials detected in ${FILES_CHECKED} production files" + pass "SA9 no credential-shaped matches found in ${FILES_CHECKED} scanned files" fi echo "" @@ -458,7 +466,7 @@ echo "" # failure — downstream consumers cannot determine the license obligations of # the code they are incorporating. # ───────────────────────────────────────────────────────────────────────────── -bold "SA10 — SPDX license headers present in all FFI source files" +bold "SA10 — SPDX header presence in enumerated FFI source files" MISSING_SPDX=0 SPDX_CHECKED=0 @@ -477,7 +485,7 @@ done if [ "$SPDX_CHECKED" -eq 0 ]; then skip_test "SA10 SPDX check" "no production FFI source files found" elif [ "$MISSING_SPDX" -eq 0 ]; then - pass "SA10 SPDX headers present in all ${SPDX_CHECKED} production FFI source files" + pass "SA10 SPDX headers found in all ${SPDX_CHECKED} enumerated FFI source files" else fail_test "SA10 ${MISSING_SPDX}/${SPDX_CHECKED} production FFI files missing SPDX headers" fi diff --git a/tests/binding_inventory.sh b/tests/binding_inventory.sh new file mode 100644 index 00000000..8115cb53 --- /dev/null +++ b/tests/binding_inventory.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# Binding directory inventory and source-policy check. This does not compile, +# link, execute, or establish cross-language ABI conformance. + +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" + +printf '%s\n' 'Binding source inventory (no language compiler/linker is invoked):' +for binding_dir in bindings/*/; do + [ -d "$binding_dir" ] || continue + language="${binding_dir#bindings/}" + language="${language%/}" + source_count="$(find "$binding_dir" -type f \ + \( -name '*.ada' -o -name '*.adb' -o -name '*.ads' -o -name '*.c' -o -name '*.h' \ + -o -name '*.cc' -o -name '*.cpp' -o -name '*.hh' -o -name '*.hpp' -o -name '*.hxx' \ + -o -name '*.cs' -o -name '*.dart' -o -name '*.ex' -o -name '*.exs' \ + -o -name '*.gleam' -o -name '*.go' -o -name '*.hs' -o -name '*.java' \ + -o -name '*.js' -o -name '*.jl' -o -name '*.kt' -o -name '*.lua' \ + -o -name '*.ml' -o -name '*.php' -o -name '*.py' -o -name '*.affine' \ + -o -name '*.rb' -o -name '*.rs' -o -name '*.swift' \) -print | wc -l | tr -d ' ')" + printf ' %-14s %s source-like files\n' "$language" "$source_count" +done + +bash tools/check-binding-policy.sh +printf '%s\n' 'Inventory and source-policy check completed; binding support/conformance was not tested.' diff --git a/tests/cross_binding_test.sh b/tests/cross_binding_test.sh deleted file mode 100644 index f737b151..00000000 --- a/tests/cross_binding_test.sh +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: MPL-2.0 -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# -# Cross-binding integration test for proven-servers. -# -# Validates that language bindings agree on ABI constants, enum encoding, -# and basic protocol semantics. Runs tests for all bindings that have -# their own test suites. -# -# Usage: bash tests/cross_binding_test.sh - -set -euo pipefail - -PASS=0 -FAIL=0 -SKIP=0 -ERRORS="" - -GREEN='\033[0;32m' -RED='\033[0;31m' -YELLOW='\033[0;33m' -NC='\033[0m' - -log_pass() { PASS=$((PASS + 1)); printf " ${GREEN}✓${NC} %s\n" "$1"; } -log_fail() { FAIL=$((FAIL + 1)); ERRORS="$ERRORS\n ✗ $1"; printf " ${RED}✗${NC} %s\n" "$1"; } -log_skip() { SKIP=$((SKIP + 1)); printf " ${YELLOW}⊘${NC} %s (skipped)\n" "$1"; } - -echo "═══════════════════════════════════════════════════════" -echo " proven-servers Cross-Binding Integration Tests" -echo "═══════════════════════════════════════════════════════" - -# ─── Core FFI Tests (Zig) ───────────────────────────────────────────────── -echo "" -echo " Core FFI (Zig):" -for module in proven-socket proven-frame proven-fsm proven-wire proven-compose proven-tls proven-config proven-audit; do - test_dir="core/$module/ffi/zig/test" - if [ -d "$test_dir" ]; then - if cd "core/$module/ffi/zig" && zig build test --summary all >/dev/null 2>&1; then - log_pass "$module" - else - log_fail "$module" - fi - cd "$OLDPWD" - else - log_skip "$module (no test dir)" - fi -done - -# ─── Protocol FFI Tests (sample 10) ────────────────────────────────────── -echo "" -echo " Protocol FFI (Zig — sample):" -SAMPLE_PROTOCOLS="proven-http proven-dns proven-mqtt proven-grpc proven-websocket proven-ssh proven-smtp proven-ntp proven-redis proven-agentic" -for proto in $SAMPLE_PROTOCOLS; do - test_dir="protocols/$proto/ffi/zig/test" - if [ -d "$test_dir" ]; then - if cd "protocols/$proto/ffi/zig" && zig build test --summary all >/dev/null 2>&1; then - log_pass "$proto" - else - log_fail "$proto" - fi - cd "$OLDPWD" - else - log_skip "$proto (no test dir)" - fi -done - -# ─── ReScript Binding Tests ────────────────────────────────────────────── -echo "" -echo " ReScript Bindings:" -if [ -d "bindings/rescript/__tests__" ]; then - # Detect ReScript test SOURCES (*_test.res), which are always present, rather - # than compiled output (*.res.js / *.res.mjs) that requires running the - # ReScript compiler in CI. ~100 source test files exist; the old *.res.js - # glob matched zero (the build emits *.res.mjs), causing a false "no tests". - rescript_tests=$(find bindings/rescript/__tests__ -name "*_test.res" 2>/dev/null | wc -l) - if [ "$rescript_tests" -gt 0 ]; then - log_pass "ReScript: $rescript_tests test files present" - else - log_fail "ReScript: no test files found" - fi -else - log_skip "ReScript (no __tests__ dir)" -fi - -# ─── Rust Binding Tests ────────────────────────────────────────────────── -echo "" -echo " Rust Bindings:" -if [ -d "bindings/rust" ]; then - rust_tests=$(find bindings/rust/src -name "*.rs" -exec grep -l "#\[test\]" {} \; 2>/dev/null | wc -l) - if [ "$rust_tests" -gt 0 ]; then - log_pass "Rust: $rust_tests files with unit tests" - else - log_fail "Rust: no test annotations found" - fi -else - log_skip "Rust (no bindings/rust dir)" -fi - -# ─── Gleam Binding Tests ───────────────────────────────────────────────── -echo "" -echo " Gleam Bindings:" -if [ -d "bindings/gleam/test" ]; then - gleam_tests=$(find bindings/gleam/test -name "*.gleam" 2>/dev/null | wc -l) - log_pass "Gleam: $gleam_tests test files present" -else - log_skip "Gleam (no test dir)" -fi - -# ─── Elixir Binding Tests ──────────────────────────────────────────────── -echo "" -echo " Elixir Bindings:" -if [ -d "bindings/elixir/test" ]; then - elixir_tests=$(find bindings/elixir/test -name "*_test.exs" 2>/dev/null | wc -l) - log_pass "Elixir: $elixir_tests test files present" -else - log_skip "Elixir (no test dir)" -fi - -# ─── Summary ───────────────────────────────────────────────────────────── -echo "" -echo "═══════════════════════════════════════════════════════" -echo " Results: $PASS passed, $FAIL failed, $SKIP skipped" -if [ "$FAIL" -gt 0 ]; then - printf " Failures:$ERRORS\n" - echo "═══════════════════════════════════════════════════════" - exit 1 -else - echo " All tests passed!" - echo "═══════════════════════════════════════════════════════" - exit 0 -fi diff --git a/tests/e2e.sh b/tests/e2e.sh index 0504f663..c3a67df5 100644 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -2,270 +2,58 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# proven-servers — End-to-End Test Suite -# -# Tests the ABI/FFI round-trip across protocols and connectors: -# 1. Zig FFI builds for core primitives -# 2. Per-connector lifecycle tests (dbconn, authconn, cacheconn, etc.) -# 3. Per-protocol FFI tests (sample of 84) -# 4. Cross-binding consistency -# 5. Safety aspect: no dangerous patterns -# 6. Binding policy: registry parity + no logic in scaffold bindings -# -# Usage: -# bash tests/e2e.sh -# just e2e +# Selected package build/test sweep. Despite the historical filename, this is +# not a full network-service E2E test or cross-language conformance suite. +# It checks two Idris2 packages, all current core/connector Zig test targets, +# and an explicitly selected sample of protocol Zig test targets. set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -cd "$PROJECT_DIR" - -PASS=0 -FAIL=0 -SKIP=0 - -green() { printf '\033[32m%s\033[0m\n' "$*"; } -red() { printf '\033[31m%s\033[0m\n' "$*"; } -yellow(){ printf '\033[33m%s\033[0m\n' "$*"; } -bold() { printf '\033[1m%s\033[0m\n' "$*"; } - -pass() { green " PASS: $1"; PASS=$((PASS + 1)); } -fail_test() { red " FAIL: $1"; FAIL=$((FAIL + 1)); } -skip_test() { yellow " SKIP: $1 ($2)"; SKIP=$((SKIP + 1)); } - -echo "═══════════════════════════════════════════════════════════════" -echo " proven-servers — End-to-End Tests" -echo "═══════════════════════════════════════════════════════════════" -echo "" - -# ─── Preflight ─────────────────────────────────────────────────────── -bold "Preflight" -if command -v zig >/dev/null 2>&1; then - green " Zig available: $(zig version)" -else - red "FATAL: zig not found" - exit 1 -fi -echo "" - -# ═══════════════════════════════════════════════════════════════════════ -# Section 0: ABI conformance (Idris is the single source of truth) -# ═══════════════════════════════════════════════════════════════════════ -bold "Section 0: ABI conformance (Idris -> generated -> Zig comptime guard)" - -# Conformance-enabled protocols: every protocol shipping an abigen ipkg -# (ABI.Emit + proven--abigen.ipkg) + a comptime guard. Auto- -# discovered so newly-onboarded protocols join without editing this script. -CONF_PROTOCOLS="$(for f in protocols/*/proven-*-abigen.ipkg; do - [ -f "$f" ] || continue - basename "$f" | sed -e 's/^proven-//' -e 's/-abigen\.ipkg$//' -done | sort | tr '\n' ' ')" - -# Generated files asserted drift-free: every _abi_gen.zig, plus the two -# reference protocols' C headers. -GEN_FILES="protocols/proven-epistemic/generated/abi/epistemic.h protocols/proven-radius/generated/abi/radius.h" -for p in $CONF_PROTOCOLS; do - GEN_FILES="$GEN_FILES protocols/proven-$p/ffi/zig/src/${p}_abi_gen.zig" -done - -if command -v idris2 >/dev/null 2>&1; then - green " Idris2 available: $(idris2 --version | head -1)" - - # 0a. epistemic engine: build + run the scenario runner (mirrors integration_test.zig). - if (cd protocols/proven-epistemic && idris2 --build proven-epistemic.ipkg >/dev/null 2>&1) \ - && ./protocols/proven-epistemic/build/exec/proven-epistemic >/dev/null 2>&1; then - pass "proven-epistemic engine conformance scenarios" - else - fail_test "proven-epistemic engine conformance scenarios" - fi - - # 0b. Regenerate all ABI artifacts from the proofs -- this builds every - # protocol's abigen (compiling its ABI proofs) -- and assert no drift. - if bash tools/gen-abi.sh >/dev/null 2>&1; then - if git diff --quiet -- $GEN_FILES; then - pass "generated ABI matches Idris proofs (no drift)" - else - fail_test "generated ABI drifted from Idris (run tools/gen-abi.sh and commit)" - fi - else - fail_test "tools/gen-abi.sh failed" - fi -else - skip_test "Idris ABI build + conformance" "idris2 not installed" -fi - -# 0d. Build each conformance protocol's Zig WITH the comptime guard active (drift -# => compile error). Works from the committed generated file even without Idris. -for p in $CONF_PROTOCOLS; do - if (cd "protocols/proven-$p/ffi/zig" && zig build >/dev/null 2>&1); then - pass "proven-$p Zig builds with ABI comptime guard" - else - fail_test "proven-$p Zig comptime guard rejected the build (ABI drift)" - fi -done -echo "" - -# ═══════════════════════════════════════════════════════════════════════ -# Section 1: Connector FFI Build + Test -# ═══════════════════════════════════════════════════════════════════════ -bold "Section 1: Connector FFI build + integration tests" - -CONNECTORS_TESTED=0 -for conn in dbconn authconn cacheconn queueconn resolverconn storageconn; do - CONN_DIR="connectors/proven-$conn/ffi/zig" - if [ -f "$CONN_DIR/build.zig" ]; then - if (cd "$CONN_DIR" && zig build 2>/dev/null); then - pass "build proven-$conn FFI" - else - fail_test "build proven-$conn FFI" - continue - fi +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" - if (cd "$CONN_DIR" && zig build test 2>/dev/null); then - pass "test proven-$conn FFI" - CONNECTORS_TESTED=$((CONNECTORS_TESTED + 1)) - else - fail_test "test proven-$conn FFI" - fi - else - skip_test "proven-$conn" "no build.zig" +for tool in idris2 zig; do + if ! command -v "$tool" >/dev/null 2>&1; then + echo "ERROR: $tool is required for the selected package test sweep" >&2 + exit 127 fi done -echo " Connectors tested: $CONNECTORS_TESTED/6" -echo "" -# ═══════════════════════════════════════════════════════════════════════ -# Section 2: Protocol FFI Build + Test (sample) -# ═══════════════════════════════════════════════════════════════════════ -bold "Section 2: Protocol FFI tests (sample of 84)" - -PROTOCOLS_TESTED=0 -PROTOCOLS_TOTAL=0 - -for proto_dir in protocols/proven-*/ffi/zig; do - [ -f "$proto_dir/build.zig" ] || continue - PROTOCOLS_TOTAL=$((PROTOCOLS_TOTAL + 1)) - - proto_name=$(echo "$proto_dir" | sed 's|protocols/proven-\(.*\)/ffi/zig|\1|') - - if (cd "$proto_dir" && zig build test 2>/dev/null); then - pass "test proven-$proto_name" - PROTOCOLS_TESTED=$((PROTOCOLS_TESTED + 1)) - else - fail_test "test proven-$proto_name" - fi - - # Limit to first 20 to keep CI time reasonable - if [ "$PROTOCOLS_TOTAL" -ge 20 ]; then - REMAINING=$(($(find protocols/proven-*/ffi/zig -name "build.zig" 2>/dev/null | wc -l) - PROTOCOLS_TOTAL)) - if [ "$REMAINING" -gt 0 ]; then - skip_test "$REMAINING more protocols" "sampled first 20" - fi - break - fi +echo "Selected package build/test sweep (not full E2E/conformance)" +echo "Idris2: $(idris2 --version | head -1)" +echo "Zig: $(zig version)" + +idris_packages=( + protocols/proven-dns/proven-dns.ipkg + protocols/proven-authserver/proven-authserver.ipkg +) +for package in "${idris_packages[@]}"; do + package_dir="$(dirname "$package")" + package_file="$(basename "$package")" + echo "==> Idris2: $package" + (cd "$package_dir" && idris2 --build "$package_file") done -echo " Protocols tested: $PROTOCOLS_TESTED/$PROTOCOLS_TOTAL (of $(find protocols/proven-*/ffi/zig -name 'build.zig' 2>/dev/null | wc -l) total)" -echo "" - -# ═══════════════════════════════════════════════════════════════════════ -# Section 3: Core Primitives FFI -# ═══════════════════════════════════════════════════════════════════════ -bold "Section 3: Core primitives" -for prim in socket frame fsm wire compose tls config audit; do - PRIM_DIR="core/proven-$prim/ffi/zig" - if [ -f "$PRIM_DIR/build.zig" ]; then - if (cd "$PRIM_DIR" && zig build test 2>/dev/null); then - pass "test core/proven-$prim" - else - fail_test "test core/proven-$prim" - fi - else - skip_test "core/proven-$prim" "no build.zig" - fi +# Run every current core and connector Zig test target. +while IFS= read -r -d '' build_file; do + build_dir="$(dirname "$build_file")" + echo "==> Zig test: $build_dir" + (cd "$build_dir" && zig build test) +done < <(find core connectors -type f -name build.zig -print0 | sort -z) + +# Keep the protocol sample explicit so the run stays bounded and auditable. +protocols=( + proven-dns proven-mqtt proven-amqp proven-authserver proven-ca + proven-pqc proven-zerotrust proven-ctlog proven-kerberos proven-backup +) +for protocol in "${protocols[@]}"; do + build_dir="protocols/$protocol/ffi/zig" + if [ ! -f "$build_dir/build.zig" ]; then + echo "ERROR: selected protocol has no Zig build manifest: $build_dir" >&2 + exit 1 + fi + echo "==> Zig test: $build_dir" + (cd "$build_dir" && zig build test) done -echo "" - -# ═══════════════════════════════════════════════════════════════════════ -# Section 4: Cross-Binding Test -# ═══════════════════════════════════════════════════════════════════════ -bold "Section 4: Cross-binding consistency" - -if [ -f "tests/cross_binding_test.sh" ]; then - if bash tests/cross_binding_test.sh 2>/dev/null; then - pass "cross-binding test suite" - else - fail_test "cross-binding test suite" - fi -else - skip_test "cross-binding" "tests/cross_binding_test.sh not found" -fi -echo "" - -# ═══════════════════════════════════════════════════════════════════════ -# Section 5: Safety Aspects -# ═══════════════════════════════════════════════════════════════════════ -bold "Section 5: Safety aspects" - -# No believe_me/assert_total in Idris2 ABI -- ACTIVE CODE ONLY. -# Exclude Idris comment lines (-- and |||) so documentation that merely *names* -# a pattern (e.g. proven-nesy/src/NeSy/Types.idr's "equivalent of believe_me" -# note) is not a false positive. A real escape hatch in code is still caught. -DANGEROUS_IDRIS=$(grep -rn 'believe_me\|assert_total\|really_believe_me' src/ connectors/*/src/ protocols/*/src/ core/*/src/ 2>/dev/null | grep -v test | grep -vE ':[0-9]+:[[:space:]]*(--|\|\|\|)' || true) -if [ -n "$DANGEROUS_IDRIS" ]; then - fail_test "Dangerous Idris2 patterns ($(echo "$DANGEROUS_IDRIS" | wc -l) occurrences)" - echo "$DANGEROUS_IDRIS" | head -5 -else - pass "No dangerous Idris2 patterns" -fi - -# No @panic in Zig FFI production code -ZIG_PANIC=$(grep -rn '@panic' connectors/*/ffi/zig/src/ protocols/*/ffi/zig/src/ core/*/ffi/zig/src/ 2>/dev/null | grep -v test || true) -if [ -n "$ZIG_PANIC" ]; then - fail_test "Zig @panic in FFI production code ($(echo "$ZIG_PANIC" | wc -l) occurrences)" -else - pass "No @panic in Zig FFI production code" -fi - -# SPDX headers -MISSING_SPDX=0 -for f in $(find connectors/*/ffi/zig/src/ protocols/*/ffi/zig/src/ -name "*.zig" 2>/dev/null | head -30); do - if ! head -3 "$f" | grep -q "SPDX"; then - MISSING_SPDX=$((MISSING_SPDX + 1)) - fi -done -if [ "$MISSING_SPDX" -eq 0 ]; then - pass "SPDX headers present (sampled 30 files)" -else - fail_test "$MISSING_SPDX files missing SPDX headers" -fi -echo "" - -# ═══════════════════════════════════════════════════════════════════════ -# Section 6: Binding policy (ADR 0003) -# ═══════════════════════════════════════════════════════════════════════ -bold "Section 6: Binding policy (registry parity + no logic in scaffolds)" - -if bash tools/check-binding-policy.sh; then - pass "binding policy (thin C-ABI wrappers; no reimplemented logic)" -else - fail_test "binding policy violation (see docs/decisions/0003-keep-bindings-thin-abi-wrappers.md)" -fi -echo "" - -# ═══════════════════════════════════════════════════════════════════════ -# Summary -# ═══════════════════════════════════════════════════════════════════════ -echo "═══════════════════════════════════════════════════════════════" -printf " Results: " -green "PASS=$PASS" | tr -d '\n' -echo -n " " -if [ "$FAIL" -gt 0 ]; then red "FAIL=$FAIL" | tr -d '\n'; else echo -n "FAIL=0"; fi -echo -n " " -if [ "$SKIP" -gt 0 ]; then yellow "SKIP=$SKIP"; else echo "SKIP=0"; fi -echo "" -echo "═══════════════════════════════════════════════════════════════" -exit "$FAIL" +echo "Selected package tests completed. This does not establish protocol conformance, ABI-wide equivalence, or production readiness." diff --git a/tests/fuzz/placeholder.txt b/tests/fuzz/placeholder.txt deleted file mode 100644 index 86212801..00000000 --- a/tests/fuzz/placeholder.txt +++ /dev/null @@ -1 +0,0 @@ -Scorecard requirement placeholder diff --git a/tests/property_test.sh b/tests/source_smoke_test.sh similarity index 54% rename from tests/property_test.sh rename to tests/source_smoke_test.sh index 2c1a50df..8855a7c6 100644 --- a/tests/property_test.sh +++ b/tests/source_smoke_test.sh @@ -2,51 +2,34 @@ # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # -# proven-servers — Property-Based Test Suite +# proven-servers — Static Source-Check Smoke Suite # -# Verifies algebraic and structural invariants that must hold across ALL -# protocol state machines in the proven-servers codebase. These are -# property tests in the shell tradition: we enumerate a representative set -# of inputs, assert the invariant for every input, and report each failure -# individually so the failing case is visible. +# This script uses source-pattern checks over a small, explicitly listed +# sample. It does not execute protocol functions and is not property-based +# testing, proof evidence, or a substitute for Zig/Idris builds and tests. # -# Properties tested -# ───────────────── -# P1 Invalid transitions are universally rejected -# For every protocol that exposes a *_can_transition function, direct -# jumps that skip intermediate states must return 0. +# Heuristics checked +# ────────────────── +# P1 Selected transition-table source lines are present/absent # -# P2 Valid initial transitions are universally accepted -# Every protocol FSM must accept its designated start edge (e.g. -# Idle → first live state). +# P2 A selected initial-edge source line is present # -# P3 Enum tag roundtrip — ABI tag identity -# For each protocol the integer tag 0..N-1 for a known enum must -# survive encode/decode through the published transition table and -# state-query functions without corruption. +# P3 Selected Zig enum declarations have an expected number of tags # -# P4 Slot exhaustion returns a sentinel, not garbage -# Calling _create beyond the pool limit must return -1 (not a -# valid slot); behaviour after exhaustion must be deterministic. +# P4 Selected create functions contain a textual -1 exhaustion branch # -# P5 ABI version is non-zero (no uninitialised protocol) -# Every protocol with an *_abi_version() function must return >= 1. +# P5 Selected ABI version functions do not visibly return zero # -# P6 Transition predicate is boolean (returns only 0 or 1) -# can_transition must never return an arbitrary integer. +# P6 Selected transition functions contain only literal 0/1 return branches # -# P7 Representative protocol Zig FFI builds compile cleanly +# P7 Selected transition functions contain a literal rejecting fallback # -# P8 State machine quiescence (terminal state or idle-return) -# Every FSM must either loop back to state 0 or have a terminal sink. -# -# P9 Invalid-slot guard present in all mutation functions -# Calling mutators with bad slot indices must not corrupt state. +# P8 Selected source files contain a slot-validator helper pattern # # Usage # ───── -# bash tests/property_test.sh -# just property-test +# bash tests/source_smoke_test.sh +# just source-smoke set -euo pipefail @@ -67,23 +50,27 @@ pass() { green " PASS: $1"; PASS=$((PASS + 1)); } fail_test() { red " FAIL: $1"; FAIL=$((FAIL + 1)); } skip_test() { yellow " SKIP: $1 ($2)"; SKIP=$((SKIP + 1)); } +# Extract one single-line-signature exported Zig function. The targeted +# functions have their closing brace at column zero in this repository. +exported_function_body() { + local function_name="$1" source_file="$2" + awk -v fn="$function_name" ' + !capture && index($0, "pub export fn " fn "(") > 0 { capture=1 } + capture { print } + capture && /^}/ { exit } + ' "$source_file" +} + echo "═══════════════════════════════════════════════════════════════" -echo " proven-servers — Property-Based Tests" +echo " proven-servers — Source-Pattern Smoke Checks (not runtime tests)" echo "═══════════════════════════════════════════════════════════════" echo "" # ───────────────────────────────────────────────────────────────────────────── -# P1 — Invalid-transition rejection property -# -# Strategy: for each protocol in our representative set, verify via source -# inspection that the can_transition function returns 0 for canonical -# skip-states cases: -# AMQP: Idle(0) → Open(3) must be 0 -# MQTT: Idle(0) → Subscribed(2) must be 0 -# DNS: QueryReceived(1) → Sent(4) must be 0 (skip ResponseBuilding) -# We also confirm that the VALID counterpart edge IS present. +# P1 — Selected source lines inside the named transition function. +# These grep-based checks are neither exhaustive nor executable verification. # ───────────────────────────────────────────────────────────────────────────── -bold "P1 — Invalid-transition rejection (per-protocol FSM)" +bold "P1 — Selected transition source lines (not runtime validation)" # Format: "proto_slug invalid_from invalid_to valid_from valid_to" declare -a P1_CASES=( @@ -106,31 +93,33 @@ for entry in "${P1_CASES[@]}"; do continue fi - # The valid pair MUST appear in the transition table. + transition_body="$(exported_function_body "${proto}_can_transition" "$SRC_FILE")" + if [ -z "$transition_body" ]; then + skip_test "P1 proven-${proto} transition table" "exported function not found" + continue + fi + + # These are source-text checks scoped to the broker/protocol transition function. VALID_PATTERN="from == ${val_from} and to == ${val_to}" - if grep -q "$VALID_PATTERN" "$SRC_FILE"; then - pass "P1 proven-${proto}: valid edge ${val_from}→${val_to} present in table" + if grep -q "$VALID_PATTERN" <<<"$transition_body"; then + pass "P1 proven-${proto}: selected valid edge ${val_from}→${val_to} appears in source" else - fail_test "P1 proven-${proto}: valid edge ${val_from}→${val_to} NOT found in table" + fail_test "P1 proven-${proto}: selected valid edge ${val_from}→${val_to} not found" fi - # The invalid pair must NOT appear as an accepted transition (return 1). INVALID_PATTERN="from == ${inv_from} and to == ${inv_to}" - if grep "$INVALID_PATTERN" "$SRC_FILE" 2>/dev/null | grep -q "return 1"; then - fail_test "P1 proven-${proto}: invalid edge ${inv_from}→${inv_to} is ACCEPTED (must be rejected)" + if grep "$INVALID_PATTERN" <<<"$transition_body" | grep -q "return 1"; then + fail_test "P1 proven-${proto}: selected invalid edge ${inv_from}→${inv_to} appears accepted" else - pass "P1 proven-${proto}: invalid edge ${inv_from}→${inv_to} correctly not accepted" + pass "P1 proven-${proto}: selected invalid edge ${inv_from}→${inv_to} not accepted by source pattern" fi done echo "" # ───────────────────────────────────────────────────────────────────────────── -# P2 — Initial-transition acceptance property -# -# Every protocol's FSM must have at least one valid outgoing transition from -# state 0 (the initial state). A protocol with no exit from state 0 is broken. +# P2 — Selected initial-transition source line. # ───────────────────────────────────────────────────────────────────────────── -bold "P2 — Initial-transition acceptance (every FSM can leave state 0)" +bold "P2 — Selected initial-transition source line" declare -a P2_PROTOCOLS=( "amqp" "dns" "mqtt" "smtp" "ftp" "cache" "ca" "agentic" @@ -144,23 +133,29 @@ for proto in "${P2_PROTOCOLS[@]}"; do continue fi - # Check for at least one accepted transition FROM state 0 to a non-zero state. - if grep -qE "from == 0 and to == [1-9]" "$SRC_FILE"; then - pass "P2 proven-${proto}: has valid initial outgoing transition from state 0" + transition_body="$(exported_function_body "${proto}_can_transition" "$SRC_FILE")" + if [ -z "$transition_body" ]; then + skip_test "P2 proven-${proto} initial transition" "exported function not found" + elif grep -qE "from == 0 and to == [1-9]" <<<"$transition_body"; then + pass "P2 proven-${proto}: initial edge appears in transition-function source" + elif grep -q 'canTransitionCheck(' <<<"$transition_body"; then + helper_body="$(awk '/^fn canTransitionCheck[(]/ {capture=1} capture {print} capture && /^}/ {exit}' "$SRC_FILE")" + if grep -qE 'from == 0 and to == [1-9]' <<<"$helper_body"; then + pass "P2 proven-${proto}: initial edge appears in delegated transition-check source" + else + fail_test "P2 proven-${proto}: no initial edge found in delegated source" + fi else - fail_test "P2 proven-${proto}: NO valid transition from initial state 0" + skip_test "P2 proven-${proto} initial transition" "implementation is not a recognized literal transition table" fi done echo "" # ───────────────────────────────────────────────────────────────────────────── -# P3 — Enum tag count correctness -# -# Property: every published enum must have the exact number of tags declared -# in the ABI spec. Extra or missing tags break the ABI contract. -# We count tag assignments (lines with '= N') in each enum block. +# P3 — Count literal enum assignments in selected Zig source declarations. +# Expected counts are local test data, not generated from Idris proofs. # ───────────────────────────────────────────────────────────────────────────── -bold "P3 — Enum tag count matches ABI spec" +bold "P3 — Selected Zig enum assignment counts (source heuristic)" # Format: "proto_slug enum_name expected_tag_count" declare -a P3_CASES=( @@ -208,11 +203,7 @@ done echo "" # ───────────────────────────────────────────────────────────────────────────── -# P4 — Slot exhaustion returns -1 sentinel -# -# Property: create() functions must validate pool capacity and return -1 when -# full. We verify this by inspecting that the source contains a '-1' return -# path in the create function body. +# P4 — Search the selected create function for a literal -1 return branch. # ───────────────────────────────────────────────────────────────────────────── bold "P4 — Slot exhaustion: create() returns -1 on pool full" @@ -228,63 +219,53 @@ for proto in "${P4_PROTOCOLS[@]}"; do continue fi - if grep -q "return -1" "$SRC_FILE" || grep -q "return @as(c_int, -1)" "$SRC_FILE"; then - pass "P4 proven-${proto}: create() has -1 exhaustion return path" + create_body="$(exported_function_body "${proto}_create" "$SRC_FILE")" + if [ -z "$create_body" ]; then + skip_test "P4 proven-${proto} create() exhaustion" "exported function not found" + elif grep -qE 'return (-1|@as\(c_int, -1\));' <<<"$create_body"; then + pass "P4 proven-${proto}: create() contains a textual -1 return branch" else - fail_test "P4 proven-${proto}: create() missing -1 exhaustion return path" + fail_test "P4 proven-${proto}: create() has no textual -1 return branch" fi done echo "" # ───────────────────────────────────────────────────────────────────────────── -# P5 — ABI version is non-zero -# -# Property: every protocol with an *_abi_version() function must return >= 1. -# ABI version 0 indicates an uninitialised or placeholder implementation. +# P5 — Look for a visible zero or positive return in ABI-version functions. # ───────────────────────────────────────────────────────────────────────────── -bold "P5 — ABI version >= 1 (no uninitialised protocols)" +bold "P5 — ABI-version return-expression source heuristic" ABI_VERSION_ZERO_COUNT=0 ABI_VERSION_POSITIVE_COUNT=0 for src in protocols/proven-*/ffi/zig/src/*.zig; do [ -f "$src" ] || continue - version=$(awk ' - /export fn.*_abi_version/ { in_fn=1 } - in_fn && /return [0-9]+;/ { - match($0, /return ([0-9]+);/, arr) - print arr[1] - in_fn=0 - } - ' "$src") - - [ -z "$version" ] && continue - - proto_name=$(basename "$(dirname "$(dirname "$(dirname "$src")")")") - if [ "$version" -ge 1 ]; then + function_name="$(basename "$src" .zig)_abi_version" + version_body="$(exported_function_body "$function_name" "$src")" + [ -z "$version_body" ] && continue + + proto_name="$(basename "$(dirname "$(dirname "$(dirname "$(dirname "$src")")")")")" + if grep -Eq 'return[[:space:]]+0;' <<<"$version_body"; then + fail_test "P5 ${proto_name}: abi_version visibly returns zero" + ABI_VERSION_ZERO_COUNT=$((ABI_VERSION_ZERO_COUNT + 1)) + elif grep -Eq 'return[[:space:]]+([1-9][0-9]*|ABI_VERSION);' <<<"$version_body"; then ABI_VERSION_POSITIVE_COUNT=$((ABI_VERSION_POSITIVE_COUNT + 1)) else - fail_test "P5 ${proto_name}: abi_version returns ${version} (must be >= 1)" - ABI_VERSION_ZERO_COUNT=$((ABI_VERSION_ZERO_COUNT + 1)) + skip_test "P5 ${proto_name} ABI version" "return expression is not recognized by this source heuristic" fi done if [ "$ABI_VERSION_POSITIVE_COUNT" -gt 0 ] && [ "$ABI_VERSION_ZERO_COUNT" -eq 0 ]; then - pass "P5 all ${ABI_VERSION_POSITIVE_COUNT} detectable ABI versions are >= 1" + pass "P5 ${ABI_VERSION_POSITIVE_COUNT} ABI-version functions have a recognized nonzero return expression" elif [ "$ABI_VERSION_POSITIVE_COUNT" -eq 0 ]; then skip_test "P5 ABI version check" "no parseable abi_version functions found" fi echo "" # ───────────────────────────────────────────────────────────────────────────── -# P6 — can_transition is a boolean predicate (returns only 0 or 1) -# -# Property: the can_transition predicate must be total and boolean — it must -# return only 0 or 1, never an arbitrary integer. We verify statically that -# all 'return' statements inside *_can_transition bodies are 'return 0;' or -# 'return 1;'. +# P6 — Check literal return lines inside selected transition functions. # ───────────────────────────────────────────────────────────────────────────── -bold "P6 — can_transition is a boolean predicate (returns only 0 or 1)" +bold "P6 — Literal return lines in selected transition functions" declare -a P6_PROTOCOLS=("amqp" "dns" "mqtt" "ca" "bfd" "smtp") @@ -295,102 +276,73 @@ for proto in "${P6_PROTOCOLS[@]}"; do continue fi - bad_returns=$(awk ' - /export fn.*_can_transition/ { in_fn=1; depth=0 } - in_fn && /\{/ { depth++ } - in_fn && /\}/ { - depth-- - if (depth == 0) { in_fn=0 } - } - in_fn && depth > 0 && /return[[:space:]]/ { - if ($0 !~ /return (0|1);/) { print NR": "$0 } - } - ' "$SRC_FILE") - - if [ -z "$bad_returns" ]; then - pass "P6 proven-${proto}: can_transition returns only 0 or 1" - else - fail_test "P6 proven-${proto}: can_transition has non-boolean returns: $(echo "$bad_returns" | head -2)" - fi -done -echo "" - -# ───────────────────────────────────────────────────────────────────────────── -# P7 — Representative protocol Zig FFI builds compile cleanly -# -# Property: every protocol in the representative set must build without errors. -# Build failure is a property violation — the FFI code is broken. -# ───────────────────────────────────────────────────────────────────────────── -bold "P7 — Zig FFI build property: representative protocols compile" - -declare -a P7_PROTOCOLS=("amqp" "dns" "mqtt") - -for proto in "${P7_PROTOCOLS[@]}"; do - FFI_DIR="protocols/proven-${proto}/ffi/zig" - if [ ! -f "$FFI_DIR/build.zig" ]; then - skip_test "P7 proven-${proto} build" "no build.zig" + transition_body="$(exported_function_body "${proto}_can_transition" "$SRC_FILE")" + if [ -z "$transition_body" ]; then + skip_test "P6 proven-${proto} transition predicate" "exported function not found" continue fi + bad_returns="$(grep -E 'return[[:space:]]' <<<"$transition_body" | grep -vE 'return[[:space:]]+(0|1);|return if .* 1 else 0;' || true)" - if (cd "$FFI_DIR" && zig build 2>/dev/null); then - pass "P7 proven-${proto}: FFI build succeeds" + if [ -z "$bad_returns" ]; then + pass "P6 proven-${proto}: transition function has only literal 0/1 return lines" else - fail_test "P7 proven-${proto}: FFI build FAILED" + fail_test "P6 proven-${proto}: transition function has unrecognized returns: $(echo "$bad_returns" | head -2)" fi done echo "" # ───────────────────────────────────────────────────────────────────────────── -# P8 — State machine quiescence property (terminal state or idle-return) -# -# Property: every FSM must either include a transition back to state 0 -# (idle reset) or have an unconditional 'return 0' fallback indicating a -# terminal sink. This prevents infinite protocol loops. +# P7 — Presence of a rejecting fallback in selected transition functions. +# This textual check does not establish liveness, reachability, or quiescence. # ───────────────────────────────────────────────────────────────────────────── -bold "P8 — State machine quiescence (terminal or idle-return)" +bold "P7 — Transition functions contain a rejecting fallback (source heuristic)" -declare -a P8_PROTOCOLS=("amqp" "dns" "mqtt" "smtp" "ca" "cache" "bfd") +declare -a P7_PROTOCOLS=("amqp" "dns" "mqtt" "smtp" "ca" "cache" "bfd") -for proto in "${P8_PROTOCOLS[@]}"; do +for proto in "${P7_PROTOCOLS[@]}"; do SRC_FILE="protocols/proven-${proto}/ffi/zig/src/${proto}.zig" if [ ! -f "$SRC_FILE" ]; then - skip_test "P8 proven-${proto} quiescence" "no src file" + skip_test "P7 proven-${proto} rejecting fallback" "no src file" continue fi - # Check for a transition TO state 0 (return to idle) anywhere in the table. - if grep -q "and to == 0) return 1" "$SRC_FILE"; then - pass "P8 proven-${proto}: FSM has idle-return path (quiesces to state 0)" - elif grep -q "^[[:space:]]*return 0;" "$SRC_FILE"; then - pass "P8 proven-${proto}: FSM has unconditional reject fallback (terminal safety)" + transition_body="$(exported_function_body "${proto}_can_transition" "$SRC_FILE")" + if [ -z "$transition_body" ]; then + skip_test "P7 proven-${proto} rejecting fallback" "exported function not found" + elif grep -qE '^[[:space:]]*return 0;' <<<"$transition_body"; then + pass "P7 proven-${proto}: transition function contains a rejecting fallback" + elif grep -q 'canTransitionCheck(' <<<"$transition_body"; then + helper_body="$(awk '/^fn canTransitionCheck[(]/ {capture=1} capture {print} capture && /^}/ {exit}' "$SRC_FILE")" + if grep -qE '^[[:space:]]*return false;' <<<"$helper_body"; then + pass "P7 proven-${proto}: delegated transition helper contains a false fallback" + else + fail_test "P7 proven-${proto}: delegated transition helper has no false fallback" + fi else - fail_test "P8 proven-${proto}: FSM missing idle-return AND unconditional reject fallback" + skip_test "P7 proven-${proto} rejecting fallback" "implementation is not a recognized literal transition table" fi done echo "" # ───────────────────────────────────────────────────────────────────────────── -# P9 — Invalid-slot guard present in all mutation functions -# -# Property: calling a state-mutation function with an invalid slot index must -# NOT corrupt any session state. The validSlot() guard pattern must be present. +# P8 — A slot-validation helper pattern is present in selected source files. +# This does not prove every exported operation calls the helper correctly. # ───────────────────────────────────────────────────────────────────────────── -bold "P9 — Invalid-slot guard present in mutation functions" +bold "P8 — Slot-validation helper pattern is present (source heuristic)" -declare -a P9_PROTOCOLS=("amqp" "dns" "mqtt") +declare -a P8_PROTOCOLS=("amqp" "dns" "mqtt") -for proto in "${P9_PROTOCOLS[@]}"; do +for proto in "${P8_PROTOCOLS[@]}"; do SRC_FILE="protocols/proven-${proto}/ffi/zig/src/${proto}.zig" if [ ! -f "$SRC_FILE" ]; then - skip_test "P9 proven-${proto} slot guard" "no src file" + skip_test "P8 proven-${proto} slot guard" "no src file" continue fi - # Acceptable guard patterns: validSlot, orelse return, or slot < 0 check. - if grep -q "validSlot\|orelse return\|slot < 0" "$SRC_FILE"; then - pass "P9 proven-${proto}: slot validation guard is present" + if grep -qE 'fn validSlot|slot[[:space:]]*<[[:space:]]*0' "$SRC_FILE"; then + pass "P8 proven-${proto}: slot-validator source pattern is present" else - fail_test "P9 proven-${proto}: NO slot validation guard found" + fail_test "P8 proven-${proto}: no slot-validator source pattern found" fi done echo "" diff --git a/tools/check-binding-policy.sh b/tools/check-binding-policy.sh index e990fcee..f87131d7 100644 --- a/tools/check-binding-policy.sh +++ b/tools/check-binding-policy.sh @@ -3,7 +3,7 @@ # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # # Binding-policy tripwire — enforces ADR 0003 -# (docs/decisions/0003-keep-bindings-thin-abi-wrappers.md): +# (docs/decisions/0003-keep-bindings-thin-abi-wrappers.adoc): # # 1. Registry parity: every bindings/ on disk is registered in # .machine_readable/BINDINGS.a2ml, and every registered language exists @@ -46,7 +46,17 @@ if [ -n "$missing" ]; then fi [ -z "${rogue}${missing}" ] && grn "OK: bindings/ ($(printf '%s\n' $ondisk | wc -w | tr -d ' ')) match registry exactly" -# --- 2. scaffold-tier bindings must stay logic-free ----------------------- +# --- 2. OCaml must remain fail-closed until compatible C stubs exist ------ +if grep -Eq '^[[:space:]]*ocaml[[:space:]]*=[[:space:]]*\{[^}]*status[[:space:]]*=[[:space:]]*"unavailable"' "$REG"; then + if grep -R -nE '^[[:space:]]*external[[:space:]]' bindings/ocaml/lib --include='*.ml'; then + red "FAIL: raw OCaml external declarations bypass the fail-closed FFI stubs" + FAIL=1 + else + grn "OK: OCaml native operations remain disabled until compatible C stubs exist" + fi +fi + +# --- 3. scaffold-tier bindings must stay logic-free ----------------------- scaffolds="$(grep -E '^[[:space:]]*[a-z]+[[:space:]]*=[[:space:]]*\{[^}]*status[[:space:]]*=[[:space:]]*"scaffold"' "$REG" \ | sed -E 's/^[[:space:]]*([a-z]+)[[:space:]]*=.*/\1/' | sort -u)" @@ -74,7 +84,7 @@ for b in $scaffolds; do done if [ "$FAIL" -ne 0 ]; then - red "Binding policy VIOLATED — see docs/decisions/0003-keep-bindings-thin-abi-wrappers.md" + red "Binding policy VIOLATED — see docs/decisions/0003-keep-bindings-thin-abi-wrappers.adoc" exit 1 fi grn "Binding policy OK." diff --git a/www/.well-known/security.txt b/www/.well-known/security.txt index fa931acd..f986f171 100644 --- a/www/.well-known/security.txt +++ b/www/.well-known/security.txt @@ -1,9 +1,8 @@ # SPDX-License-Identifier: MPL-2.0 -# RFC 9116 - security.txt -# https://securitytxt.org/ +# RFC 9116 security contact for proven-servers Contact: mailto:j.d.a.jewell@open.ac.uk -Expires: 2026-12-31T23:59:59.000Z +Expires: 2027-09-27T23:59:59.000Z Preferred-Languages: en Canonical: https://github.com/hyperpolymath/proven-servers/.well-known/security.txt -Policy: https://github.com/hyperpolymath/proven-servers/blob/main/SECURITY.md +Policy: https://github.com/hyperpolymath/proven-servers/blob/main/SECURITY.adoc