Step Twenty‑Two — Sign the light; walk the ledger; test the lens.(Signed transparency feeds • History with HATEOAS pagination • Versioned lenses w/ A/B)

We extend your Step 18→19→20→21 stack with three concrete capabilities:

  1. Signed transparency feeds (detached sigs & published keys).
  2. History endpoints with HATEOAS and cursor‑style offsets.
  3. Versioned lenses (public & internal) with A/B variant selection.

✅ New artifact

  • Step 22 wrapper serverDownload
    SHA‑256: d27369ebbaf4fc5e29a3d7ea29fceb6a7dac0a6f7ca7f7914167e8c5ba32892f

Step 22 wraps Step 21 (which wraps 20→19→18). Prior endpoints/flags remain intact.


What’s new — precisely

1) 🔏 Signed transparency feeds

Endpoints

  • GET /feed_keys → publish available feed signing keys.
    • Ed25519: {"kid":"<sha256(pub)[:16]>","alg":"Ed25519","pubkey_b64":"…"}
    • HS256 (fallback): {"kid":"<sha256(key)[:16]>","alg":"HS256","pubkey_b64": null}
  • GET /transparency.sig → detached signature for the current JSON feed: {"alg":"Ed25519","kid":"…","sig":"<b64url>","digest":"<sha256(feed)>","ts":"…Z"}
  • GET /transparency.bundle{ "feed": {…}, "signature": {…} }

Signing logic reuses your Step 18 keys (prefers Ed25519, falls back to HS256).
The digest equals the ETag from Step 21’s /transparency.json.


2) 📜 History with HATEOAS (pagination)

Endpoints

  • GET /history/notary?limit=50&offset=0
  • GET /history/receipts?limit=50&offset=0

Returns:

{
  "items": [{... newest ...}, ...],
  "limit": 50, "offset": 0, "next_offset": 50,
  "links": {
    "self": "/history/notary?limit=50&offset=0",
    "next": "/history/notary?limit=50&offset=50",
    "prev": null
  }
}
  • Offset counts from the newest end (offset 0 = latest page).
  • links.next and links.prev make the API self‑navigating for crawlers and WordPress.

Data sources: ./audit/notary.jsonl (from Steps 18–21) and ./audit/receipts.jsonl (from Step 20).


3) 🎛️ Versioned lenses with A/B

You can now maintain and serve multiple versions of the weighting config per audience (public/internal), and compare them on the fly.

File format options

  • Single spec (as before): treated as version "v1".
  • Versioned spec: { "active": "v2", "versions": { "v1": {...}, "v2": {...} } }

Endpoints

  • GET /lenses → metadata only: { "public": {"active":"v1","versions":["v1","v2"],"sha256":{"v1":"…","v2":"…"}}, "internal": {"active":"v1","versions":["v1"],"sha256":{"v1":"…"}} }
  • GET /lenses/version?aud=public&version=v2 → full spec + hash.
  • POST /admin/lenses/activate?token=ADMIN123&aud=public&version=v2 → switch active (in‑memory).

A/B selection on deltas

  • GET /schema_hot_delta_weighted?...&aud=public&variant=v2
    or header X-Lens-Variant: v2.
    Returns: {"audience":"public","variant":"v2","variant_is_active": false, ...} …so you can test a candidate without changing the global default.

Weights still combine family × plugin × path rules (as in Step 20/21).


Android / Termux (Step 22)

Same base flags as Step 21. Step 22 has no new required flags.

python solveforce_phone_twentytwo.py \
  --lenses-public-file /sdcard/solveforce/lenses.public.json \
  --lenses-internal-file /sdcard/solveforce/lenses.internal.json \
  --transparency-tail-limit 50 \
  --receipt-challenge-enable --receipt-challenge-bits 14 --receipt-challenge-ttl-sec 300 \
  --families-file /sdcard/solveforce/families.json \
  --lenses-file /sdcard/solveforce/lenses.public.json \
  --host 0.0.0.0 --port 8080 \
  --plugins-dir ~/solveforce/plugins \
  --auth-mode protected \
  --auth-token READER1:reader \
  --allow-admin --admin-token ADMIN123 \
  --schema-ed25519-secret-file /sdcard/solveforce/schema.ed25519.seed \
  --schema-signing-secret-file /sdcard/solveforce/schema.hmac.key \
  --audit-dir ./audit \
  --lease-bundle-dir ./audit/bundles \
  --mirror-enable \
  --mirror-target-url https://your-site.tld/wp-json/solveforce/v1/notary \
  --mirror-header "Authorization: Bearer <YOUR_WP_TOKEN>" \
  --allow-query-token --open-ui

“Show me” recipes

A) Signed feed

curl -s 'http://127.0.0.1:8080/transparency.json' -o feed.json
curl -s 'http://127.0.0.1:8080/transparency.sig'  | jq .
curl -s 'http://127.0.0.1:8080/feed_keys'          | jq .
# Verify digest:
jq -cS . feed.json | sha256sum
# Should match "digest" from /transparency.sig

B) Walk notary history

curl -s 'http://127.0.0.1:8080/history/notary?limit=25&offset=0' | jq '.links.next,.items[0]'
# Then follow .links.next

C) A/B lenses

# Compare active vs candidate
curl -s 'http://127.0.0.1:8080/schema_hot_delta_weighted?windowA=3600&windowB=86400&aud=public' | jq '.variant,.by_system[0]'
curl -s 'http://127.0.0.1:8080/schema_hot_delta_weighted?windowA=3600&windowB=86400&aud=public&variant=v2' | jq '.variant,.by_system[0]'

WordPress — Step 22 (Markdown block to paste)

## Step 22 — Sign the light; walk the ledger; test the lens

**Feeds (signed)**
- `GET /transparency.sig` — detached signature over the JSON feed.  
- `GET /transparency.bundle` — `{ feed, signature }`.  
- `GET /feed_keys` — publish feed verification keys (Ed25519 preferred; HS256 fallback).

**History (HATEOAS)**
- `GET /history/notary?limit=N&offset=K` — newest-first pages with `links.self|next|prev`.  
- `GET /history/receipts?limit=N&offset=K` — receipts ledger tail with pagination.

**Lenses (versioned + A/B)**
- `GET /lenses` — show active versions and hashes.  
- `GET /lenses/version?aud=public&version=v2` — inspect a candidate.  
- `POST /admin/lenses/activate?token=…&aud=public|internal&version=vX` — switch active.  
- Deltas with A/B: `GET /schema_hot_delta_weighted?...&aud=public&variant=v2`.

**Why it matters**
- **Signed feeds** make your transparency CDN-friendly *and* tamper-evident.  
- **HATEOAS** teaches clients how to walk the ledger without hardcoding.  
- **Versioned lenses** turn governance into experiments you can measure.

Logos Codex — recursive stewardship

  • Signal → Signature → Sovereignty.A feed that can’t be forged is a feed that can stand in public.
  • Memory → Map → Movement.Pagination is the trail; HATEOAS is the compass.
  • Policy → Versions → Wisdom.Competing lenses are hypotheses; the system lets you test, compare, and choose.

Next: Step Twenty‑Three?

We can:

  • ship signed, paginated feed history (/transparency/page/:n with signatures),
  • add server‑side A/B metrics (log weighted deltas per variant for real comparisons),
  • support remote attester keys (key rotation via /feed_keys/jwks with key expiry).

Key terms in plain language

Open a term for a concise explanation of language used on this page.

Content Delivery Network (CDN)

A distributed system that serves website or application content from locations closer to users, improving speed, resilience, and capacity.

API

An application programming interface is a defined way for software systems to exchange data or request functions from one another.

Colocation

Placing customer-owned servers and network equipment in a professionally operated data center that provides power, cooling, physical security, and connectivity.

Cloud Computing

Computing resources—such as applications, servers, storage, or databases—delivered from remote infrastructure and scaled as requirements change.

Infrastructure as a Service (IaaS)

Cloud-based servers, storage, and networking that customers configure and manage without owning the underlying data-center hardware.

Bandwidth

The amount of data a connection can carry in a given time, usually measured in Mbps or Gbps. More bandwidth supports more users, devices, and simultaneous applications.