diff --git a/next/genesis/manifest.sx b/next/genesis/manifest.sx index 9847d6cf..cba82f45 100644 --- a/next/genesis/manifest.sx +++ b/next/genesis/manifest.sx @@ -38,7 +38,9 @@ "projections/actor-state.sx" "projections/define-registry.sx" "projections/audience-graph.sx") - :validators () + :validators ("validators/envelope-shape.sx" + "validators/signature.sx" + "validators/type-schema.sx") :codecs () :sig-suites () :audience ()) diff --git a/next/genesis/validators/envelope-shape.sx b/next/genesis/validators/envelope-shape.sx new file mode 100644 index 00000000..e7e4bb2d --- /dev/null +++ b/next/genesis/validators/envelope-shape.sx @@ -0,0 +1,22 @@ +;; next/genesis/validators/envelope-shape.sx +;; +;; Validates required envelope fields per design §3.1. Stage 1 of +;; the validation pipeline (Step 6). Mirrors the kernel's +;; envelope:validate_shape/1 from Step 2a — when the pipeline runs +;; in OCaml-side sandbox eval mode it dispatches by name; when it +;; runs through the kernel Erlang path it short-circuits to the BIF. + +(DefineValidator + :name "envelope-shape" + :doc "Required-fields check on the activity envelope:\n :id, :type, :actor, :published, :signature must all be\n present and non-nil. The :signature sub-field needs\n :key_id, :algorithm, :value." + :predicate (fn + (act) + (and + (not (nil? (-> act :id))) + (not (nil? (-> act :type))) + (not (nil? (-> act :actor))) + (not (nil? (-> act :published))) + (not (nil? (-> act :signature))) + (not (nil? (-> act :signature :key_id))) + (not (nil? (-> act :signature :algorithm))) + (not (nil? (-> act :signature :value)))))) diff --git a/next/genesis/validators/signature.sx b/next/genesis/validators/signature.sx new file mode 100644 index 00000000..184cec35 --- /dev/null +++ b/next/genesis/validators/signature.sx @@ -0,0 +1,13 @@ +;; next/genesis/validators/signature.sx +;; +;; Stage 2 of the validation pipeline per design §14. Verifies the +;; activity signature against the time-relevant public key in the +;; actor-state projection. Bootstrap entry; the kernel dispatches +;; to envelope:verify_signature/2 (Step 2c) when running in +;; Erlang-on-SX mode. Per design §9.6 the lookup is timestamp-aware +;; — key validity is evaluated at :published, not "now". + +(DefineValidator + :name "signature" + :doc "Signature verification. Picks the signature suite by\n :signature :algorithm, fetches the key with id ==\n :signature :key_id that was active at :published from\n the actor-state projection, then dispatches to the\n suite's :verify body." + :predicate (fn (act) true)) diff --git a/next/genesis/validators/type-schema.sx b/next/genesis/validators/type-schema.sx new file mode 100644 index 00000000..b5f517a0 --- /dev/null +++ b/next/genesis/validators/type-schema.sx @@ -0,0 +1,21 @@ +;; next/genesis/validators/type-schema.sx +;; +;; Stage 5 of the validation pipeline per design §14. Validates +;; the activity's :object against the schema registered for its +;; :object :type in the define-registry projection. + +(DefineValidator + :name "type-schema" + :doc "Looks up the object-type registration in the\n define-registry projection, fetches its :schema body,\n and evaluates it against (-> act :object). Returns true\n when no object-type is named (some verbs carry no\n :object) or when no schema is registered for the named\n type (open-world default — Step 6 may tighten)." + :predicate (fn + (act) + (let + ((obj (-> act :object))) + (cond + (nil? obj) + true + (nil? (-> obj :type)) + true + :else (let + ((schema (-> (registry-lookup :object-types (-> obj :type)) :schema))) + (if (nil? schema) true (apply-schema schema obj))))))) diff --git a/next/tests/genesis_parse.sh b/next/tests/genesis_parse.sh index ff533be1..913a68ee 100755 --- a/next/tests/genesis_parse.sh +++ b/next/tests/genesis_parse.sh @@ -3,7 +3,7 @@ # # Confirms the seed genesis SX files parse cleanly and have the # expected top-level head form. The bundler (Step 4c+) consumes -# these forms directly as data. 31 cases. +# these forms directly as data. 36 cases. set -uo pipefail cd "$(git rev-parse --show-toplevel)" @@ -84,6 +84,16 @@ cat > "$TMPFILE" <<'EPOCHS' (eval "(get (apply dict (rest (parse (file-read \"next/genesis/projections/audience-graph.sx\")))) :name)") (epoch 58) (eval "(len (get (apply dict (rest (parse (file-read \"next/genesis/manifest.sx\")))) :projections))") +(epoch 60) +(eval "(first (parse (file-read \"next/genesis/validators/envelope-shape.sx\")))") +(epoch 61) +(eval "(get (apply dict (rest (parse (file-read \"next/genesis/validators/envelope-shape.sx\")))) :name)") +(epoch 62) +(eval "(get (apply dict (rest (parse (file-read \"next/genesis/validators/signature.sx\")))) :name)") +(epoch 63) +(eval "(get (apply dict (rest (parse (file-read \"next/genesis/validators/type-schema.sx\")))) :name)") +(epoch 64) +(eval "(len (get (apply dict (rest (parse (file-read \"next/genesis/manifest.sx\")))) :validators))") EPOCHS OUTPUT=$(timeout 30 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -138,6 +148,11 @@ check 55 "actor-state.sx name" "actor-state" check 56 "define-registry.sx name" "define-registry" check 57 "audience-graph.sx name" "audience-graph" check 58 "manifest has 7 projections" "7" +check 60 "envelope-shape.sx head form" "DefineValidator" +check 61 "envelope-shape.sx name" "envelope-shape" +check 62 "signature.sx name" "signature" +check 63 "type-schema.sx name" "type-schema" +check 64 "manifest has 3 validators" "3" TOTAL=$((PASS+FAIL)) if [ $FAIL -eq 0 ]; then diff --git a/plans/fed-sx-milestone-1.md b/plans/fed-sx-milestone-1.md index dddb5f8a..3da23403 100644 --- a/plans/fed-sx-milestone-1.md +++ b/plans/fed-sx-milestone-1.md @@ -248,7 +248,7 @@ replay(LogState, InitAcc, Fun) -> ... - [x] **4b-act** — Remaining activity-types: `update.sx` + `delete.sx`, manifest updated, parse tests (10 cases total in `genesis_parse.sh`) - [x] **4b-obj** — Object-types: SXArtifact, Note, Tombstone, DefineActivity, DefineObject, DefineProjection, DefineValidator, DefineCodec, DefineSigSuite, Snapshot — 10 `DefineObject` files + manifest updated + 12 new parse tests - [x] **4b-proj** — Projections: activity-log, by-type, by-actor, by-object, actor-state, define-registry, audience-graph — 7 `DefineProjection` files + manifest updated + 9 new parse tests -- [ ] **4b-vld** — Validators: envelope-shape, signature, type-schema +- [x] **4b-vld** — Validators: envelope-shape, signature, type-schema — 3 `DefineValidator` files + manifest updated + 5 new parse tests - [ ] **4b-cod** — Codecs + sig-suites + audience predicates - [ ] **4c** — `bootstrap:read_genesis/1` in Erlang: walk the manifest, file-read each referenced .sx, return parsed forms - [ ] **4d** — `bootstrap:build_genesis/1` + `bootstrap:verify_genesis/1`: compute bundle CID over the read forms via the host `cid:to_string` substrate; verify against a stored `bundle.cidhash` @@ -955,6 +955,7 @@ A few things still under-specified; resolve as work begins. Newest first. One line per sub-deliverable commit. Erlang conformance gate (`bash lib/erlang/conformance.sh`) must remain 729/729 on every entry. +- **2026-05-27** — Step 4b-vld: bootstrap validators complete — 3 `DefineValidator` SX files (envelope-shape mirroring Step 2a, signature stub delegating to envelope:verify_signature/2 per design §9.6, type-schema looking up the object-type schema from define-registry). Manifest `:validators` populated; `next/tests/genesis_parse.sh` 36/36. Erlang conformance 729/729. - **2026-05-27** — Step 4b-proj: bootstrap projections complete — 7 `DefineProjection` SX files authored (activity-log identity, by-type/by-actor/by-object indexes, actor-state with key history fold, define-registry meta-fold over Create{Define*}, audience-graph stub). Manifest `:projections` populated; `next/tests/genesis_parse.sh` 31/31. Erlang conformance 729/729. - **2026-05-27** — Step 4b-obj: bootstrap object-types complete — 10 `DefineObject` SX files authored (SXArtifact, Note, Tombstone, DefineActivity, DefineObject, DefineProjection, DefineValidator, DefineCodec, DefineSigSuite, Snapshot). Each carries an SX `:schema` predicate. Manifest `:object-types` populated; `next/tests/genesis_parse.sh` 22/22. Erlang conformance 729/729. - **2026-05-27** — Step 4b-act: bootstrap activity-types complete — `update.sx` (Update verb, requires :object CID + :patch) + `delete.sx` (Delete verb, requires :object CID) authored as DefineActivity forms matching the Create shape. Manifest updated; `next/tests/genesis_parse.sh` 10/10. Step 4b broken into act/obj/proj/vld/cod sub-deliverables on the plan. Erlang conformance 729/729.