Skip to content

Data Schematic: Replace Provenance free-text info with TypedStandards cryptographic signature #4448

Description

@jqnatividad

Currently, the provenance info is just free-text.

Replace it with TypedStandards.org instead.

cc @npstorey

Activity

  1. jqnatividad commented on Aug 27, 2026

    @jqnatividad
    CollaboratorAuthor

    Investigation: what replacing free-text provenance with TypedStandards actually entails

    (Investigated by Fable 5 against npstorey/typedstandards main as of 2026-08-26 and the Typed Standards Specification v0.1 Working Draft in npstorey/civic-ai-tools.)

    Where the free-text provenance lives today

    There are three provenance surfaces in a Data Schematic, not one:

    1. Info-drawer metadata table (src/cmd/viz.rs): Generated by: qsv <version> + Compiled: timestamp + the optional --dataset-pid link. This is what satisfies MUST 9 (Provenance) of docs/DATA_SCHEMATIC.md — and it is pure, unverifiable text.
    2. Data Dictionary drawer footer: the x-qsv.generated_by attribution block authored by describegpt (command line, prompt file, model, LLM API URL, language, timestamp), stored verbatim in the JSON Schema and localized at render time.
    3. The documented Level 3 gap (docs/DATA_SCHEMATIC.md, "Level 3 is not reached today"): --dict-info bundles the schema, the charted frequency counts and the consumed stats sidecars into the HTML, but the bundled inputs carry no integrity digests — exactly the attestation MUST 8 requires.

    What TypedStandards offers (current state)

    • The format: a record package is a single canonical JSON object. Chain (spec §8.2/§8.3.1): unsigned envelope → RFC 8785 JCS canonicalization → SHA-256 envelope hash → Ed25519ph signature computed over the UTF-8 bytes of the hex string of that hash (a deliberate spec quirk that any independent implementation must match exactly). The signature envelope is {signature, publicKey (base64 DER SPKI), algorithm: "Ed25519ph", kid}, with kid resolvable in a publisher's trust registry (§8.3.3).
    • Timestamps / transparency: RFC 3161 (freetsa.org) + Sigstore Rekor inclusion proofs are SHOULD / best-effort (§8.3.2). Important for viz: these are the only network legs, and they're optional — offline generation survives.
    • Content hashing: contentHash is a multihash digest set — sha256 required, blake3 a registered alternate (qsv already ships blake3).
    • Reference implementations are TypeScript/npm: @typedstandards/verify-core and @typedstandards/produce-core. produce-core is deliberately I/O-free with caller-supplied keys and byte-golden fixtures, explicitly so that "an independent implementation can emit packages that verify under spec §9.2" — i.e. a Rust producer inside qsv is an intended use case, and the byte-golden fixture suite doubles as cross-language test vectors.
    • Verification UX: typedstandards.org /verify resolves bare identifiers against bareIdentifierHost; cross-host links are /verify/<host>/<id>. Crucially, the §8.8 commitment view with ?inline=1 is a self-contained bundle that verifies with zero network access (§9.4) — the relevant pattern for a locally-generated CLI artifact that has no hosting origin.

    The catch: content-profile mismatch

    v0.1 defines exactly one content profile — literally named datHere (§8.7) — but it is the A–G envelope for AI-assisted chat analysis. Its normative requirements (§8.7.1) include prompt.text with full visibility, a non-empty system prompt (skillMetadata.skillText), a deliberative trace, a Jupyter notebook (section E), and a rendered answer. A Data Schematic satisfies essentially none of that — MUST 1 of docs/DATA_SCHEMATIC.md says panels are cache-derived, not model-authored.

    There are really two distinct artifacts here, with different fits:

    Artifact Producer TypedStandards fit
    The data dictionary describegpt (LLM-produced) Maps naturally onto the AI-assisted-analysis story: prompt / model / output = the JSON Schema. Closest to the existing datHere profile, though still not a chat trace + notebook.
    The schematic itself deterministic derivation from the stats & frequency caches Needs a content profile that doesn't exist yet. Even the reserved Producer Profile types (Human, Hybrid, Sandbox-only) don't cover "deterministic tool output".

    The spec says new profiles land "naming the motivating adopter per the Xanadu doctrine" — qsv would be that motivating adopter. So the first step is upstream coordination (cc @npstorey), not qsv code: spec a data-schematic (or similar) content profile plus a canonicalization rule for what the content hash fingerprints (the natural candidate: the dataset + every --dict-info bundled input).

    Feasibility in Rust

    All primitives exist as maintained crates: serde_json_canonicalizer (RFC 8785 JCS), ed25519-dalek (sign_prehashed = Ed25519ph per RFC 8032 §5.1), sha2, and blake3 already in-tree. The format layer is small. The real design work is:

    • Key custody: who signs a locally-generated dashboard? Likely --sign-key <file> / env var, with the publisher (a portal, an org) hosting the trust registry at their origin. Not signing must stay first-class (produce-core treats it that way too): an unsigned envelope + envelope hash already delivers content-addressed integrity without identity.
    • Identifier / verify story: a CLI artifact has no origin, so the inline commitment-view bundle (§9.4 zero-network verification) is the natural default, with hosted /verify/<host>/<id> links for publishers who serve their packages.

    Suggested phasing

    1. Phase 0 (upstream, blocking) — with @npstorey: spec a Data Schematic content profile + canonicalization-rule URI; settle the CLI-artifact identifier/verify story; decide whether the describegpt dictionary gets its own (AI-assisted) package or rides inside the schematic package.
    2. Phase 1 (qsv, no keys required) — compute multihash digests (sha256 + blake3) for the dataset and every --dict-info bundled sidecar; emit an unsigned JCS envelope (embedded and/or .package.json sidecar); surface the envelope hash in the meta table alongside — eventually instead of — the free-text row. This alone closes the documented Level 3 gap in DATA_SCHEMATIC.md and is valuable even if signing never lands.
    3. Phase 2 (signing) — Ed25519ph via --sign-key; signature envelope embedded in the HTML + sidecar; /verify link or inline commitment bundle; vendor produce-core's byte-golden fixtures as interop tests so the Rust producer provably emits packages verify-core accepts.
    4. Phase 3 (optional, network) — RFC 3161 timestamp + Rekor inclusion behind a flag, preserving offline generation by default.

    One framing note

    "Replace" may be too strong for the human-readable rows: the meta table's Generated by: / Compiled: lines are the rendered provenance a reader sees, and TypedStandards itself keeps human-readable fields alongside the signed envelope. The likely end state is the free text backed by (and re-derived from) the signed package, rather than deleted.

  2. jqnatividad commented on Sep 13, 2026

    @jqnatividad
    CollaboratorAuthor

    Revisit once TypedStandards supports something like Decentralized Identifiers

    https://www.w3.org/TR/did/

  3. added
    revisit-laterclosed for now, but will revisit later
    enhancementNew feature or request. Once marked with this label, its in the backlog.
    on Sep 13, 2026
  4. npstorey commented on Sep 22, 2026

    @npstorey

    Revisit once TypedStandards supports something like Decentralized Identifiers

    https://www.w3.org/TR/did/

    Released:

    Against your Phase 0:

    1. Content profile. No new contentProfile value (ADR-0029 §5): the profile is named by producerProfile: "scripted-recomputation/<subtype>", with a subtype you name, and metadata.contentProfile is omitted or "default". metadata.captureMethod: "tool-emitted": the program that computed the content writes the package itself, hashing the bytes as it writes them.
    2. Canonicalization-rule URI. contentCanonicalization: "https://typedstandards.org/canonicalization/raw-bytes/v1". contentHash.sha256 is the SHA-256 of the output file's exact bytes, the digest sha256sum prints.
    3. CLI-artifact identifier/verify story. signer.identifier is a did:key (Ed25519) derived from the signing key, with bindingTier: "pseudonymous". No domain and no trust registry needed. A verifier recomputes the identifier from the key and reports trust status self_certified: one key signed everything under that identifier, which says nothing about who holds the key. A package signed this way verifies with no network access.
    4. Whether the describegpt dictionary gets its own package stays your call.

    For interop tests:

    The public verifier at typedstandards.org shows a did:key signer's package as self-certified, never as a known publisher.

    Limits:

    • No real package is signed with did:key yet.
    • No package carries tool-emitted yet; the q15 fixture carries script-run, and verify-core 0.10.0 accepts both. ADR-0029 rests tool-emitted on your published Phase 1.
    • raw-bytes/v1 covers one output file. A rule over a set of files (the dataset + every --dict-info input) is not settled.
    • sha256 is required. A producer MAY list blake3 alongside it, but v0.1 verifiers check sha256 only.
    • did:key has no rotation and no revocation. A compromised key can only be abandoned.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    AIFAIRificationenables blazing-fast, "automagical" FAIRificationenhancementNew feature or request. Once marked with this label, its in the backlog.revisit-laterclosed for now, but will revisit later

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions