TERMINAL://boot.sys
RESUMESCAN PRO v4.2.0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
> Initializing neural network...
> Loading ATS bypass protocols...
> Connecting to AI providers...
├── OpenAI GPT-4 ............ [OK]
├── Anthropic Claude ....... [OK]
└── Google Gemini .......... [OK]
> Calibrating keyword scanner...
> Mounting secure storage...
> Encryption protocols active
SYSTEM READY
[ PRESS ANY KEY TO CONTINUE ]
RESUMESCAN PROPVAIO v1.0 · spec

PVAIO_PROTOCOL

An open, cryptographically-verifiable format for AI-rewritten resumes. Implementable by anyone. Verifiable by anyone. No licence fee, no hosted dependency, no vendor lock-in.

01. What is PVAIO?

PVAIO — "Provenance-Verified AI Optimization" — is a protocol for proving, in a way anyone can independently check, that every sentence in an AI-rewritten resume was produced against a specific original, under a specific policy, by a specific engine.

The core primitive is an anchor ledger: every generated span is classified against the source text (exact / fuzzy / semantic / fabricated), hashed, committed to a Merkle tree, and signed. The ledger travels with the resume — embedded in PDF metadata, exported as HR-JSON 4, stamped into a public badge. Given a ledger and a public key, a verifier can tell you with cryptographic certainty: "this text was produced by PVAIO v2.x against that resume, and no byte has been changed since."

02. Protocol overview

The entire data flow for a PVAIO-signed artifact:

   ┌────────────────┐        ┌────────────────┐
   │  ORIGINAL      │        │  JOB           │
   │  RESUME        │        │  DESCRIPTION   │
   └──────┬─────────┘        └────────┬───────┘
          │                           │
          ▼                           ▼
    normalize()                 normalize()
          │                           │
          └─────────────┬─────────────┘
                        ▼
                 ┌─────────────┐
                 │  REWRITER   │   (any LLM)
                 └──────┬──────┘
                        ▼
                OPTIMIZED TEXT
                        │
                        ▼
              resolveAnchors(orig, opt)
                        │
                        ▼
               Anchor[] ──► leafHash[]
                                │
                                ▼
                         merkleRoot
                                │
                  ┌─────────────┴─────────────┐
                  ▼                           ▼
           HMAC-SHA256(secret)        Ed25519(priv-key)
                  │                           │
                  └──────────┬────────────────┘
                             ▼
                    PROVENANCE LEDGER
                 (merkleRoot, signature, pubkeySig,
                  policyMode, issuedAt, stats)
                             │
                             ▼
                   HR-JSON 4 bundle
                   ( + optimizedText
                     + originalText [optional]
                     + publicKeyPem )

03. Data model

An Anchor ties one generated span to one source span:

type AnchorKind = "exact" | "fuzzy" | "semantic" | "fabricated";

interface Anchor {
  genStart:   number;           // offset into optimizedText
  genEnd:     number;
  srcStart:   number | null;    // offset into originalText (null if fabricated)
  srcEnd:     number | null;
  kind:       AnchorKind;
  confidence: number;           // 0..1
  leafHash:   string;           // 64 hex chars (sha256)
}

A LedgerEnvelope is what gets signed and shipped:

interface LedgerEnvelope {
  engineVersion:       string;                    // e.g. "2.7.0"
  resumeHash:          string;                    // sha256(canonicalize(optimizedText))
  jobHash:             string;                    // sha256(canonicalize(jobText))
  policyMode:          "strict" | "normal" | "permissive";
  merkleRoot:          string;                    // root over anchor.leafHash[]
  signature:           string;                    // HMAC-SHA256 hex
  pubkeySig?:          string;                    // Ed25519 hex (v2.0+)
  pubkeyFingerprint?:  string;                    // first 16 hex of sha256(DER pubkey)
  issuedAt:            string;                    // ISO8601 UTC
  nonce?:              string;                    // anti-replay (v2.6+, base64url)
  validUntil?:         string;                    // ISO8601 expiry (v2.6+)
  anchors:             Anchor[];
  stats:               Record<AnchorKind, number>;
  fabricatedFreeProof?: FabricatedFreeProof;      // v2.7+; see §12
}

Leaf-hash formulas — identical bytes on the wire for every PVAIO implementer:

leafHash(anchor) = sha256(
  generatedSpan + "|" +
  ( anchor.kind === "exact"
      ? originalText.slice(srcStart, srcEnd)
      : (srcStart ?? "-") + "-" + (srcEnd ?? "-")
  )
)

// Where:
//   generatedSpan = optimizedText.slice(genStart, genEnd)
//   "canonicalize" = NFC-normalize; strip zero-widths; fold NBSP → space;
//                    normalize curly quotes/dashes. See
//                    src/lib/pvaio/normalize.ts for the byte-exact rules.

04. Merkle construction

Bitcoin-style. Leaves are the leafHash bytes (NOT the hex strings — unhex first). At each level, pair and hash sha256(left ∥ right); if the level has an odd count, duplicate the last leaf. Root is a 32-byte digest (64 hex chars).

function merkleRoot(leafHexHashes: string[]): string {
  if (leafHexHashes.length === 0) return "0".repeat(64);
  let level = leafHexHashes.map(h => Buffer.from(h, "hex"));
  while (level.length > 1) {
    const next = [];
    for (let i = 0; i < level.length; i += 2) {
      const left  = level[i];
      const right = i + 1 < level.length ? level[i + 1] : left;  // duplicate
      next.push(sha256(Buffer.concat([left, right])));
    }
    level = next;
  }
  return level[0].toString("hex");
}

05. Signing

Two signature layers over the same canonical payload:

canonicalPayload = [
  engineVersion,
  resumeHash,
  jobHash,
  merkleRoot,
  sha256("policy:" + policyMode),                    // ONLY if policyMode is set (v1.2+)
  "pvaio26:" + issuedAt + ":" + nonce + ":" + validUntil,
                                                     // ONLY if any of {nonce, validUntil} is set (v2.6+)
  "pvaio27:" + fabricatedFreeProof.claimRoot         // ONLY if fabricatedFreeProof is set (v2.7+)
].join("|")

signature  = hex( HMAC-SHA256(signingSecret, canonicalPayload) )
pubkeySig  = hex( Ed25519(privateKey,       canonicalPayload) )

Each optional block is appended only when the envelope actually carries its fields. Absence keeps the canonical bytes byte-identical to the earlier version, so every pre-v2.6 or pre-v2.7 ledger continues to verify unchanged against the same pubkey after an engine bump.

HMAC is the hosted check — only the issuing deployment can mint it, which is exactly what you want for "is this ledger one we actually issued." Ed25519 is the offline check — any third party who has the deployment's public key can verify the ledger without contacting the issuer at all. Both checks are over the same bytes; tampering with any field invalidates both.

Public keys are published at /.well-known/pvaio-keys.json as a list (key-rotation-friendly). Each key carries a 16-hex fingerprint matched to ledger.pubkeyFingerprint.

06. Policy modes

Policy is a pre-commitment the candidate makes before generation — not a post-hoc score. Three modes, cryptographically bound via sha256("policy:" + mode) in the canonical payload:

strict      — refuse to produce ANY fabricated anchor. If the model drifts,
              the system tells the candidate to regenerate or edit by hand.
              A strict-mode ledger is the strongest possible attestation:
              "I have source for every sentence."

normal      — generate freely, but block export until the candidate types
              an affirmation phrase for any fabricated spans. (v1 default.)

permissive  — warn visibly in the UI; never block. Useful for drafts and
              comparisons the candidate intends to edit by hand.

Verifiers treat policyMode as advisory for their own UI but MUST include it in the canonical payload when recomputing the signature. A ledger cannot be "rebranded" from permissive → strict without invalidating both signatures.

07. Verification procedure

Given a bundle (HR-JSON 4 or naked envelope), a compliant verifier performs:

1. Parse the envelope. Require:
     merkleRoot, signature, engineVersion, resumeHash, jobHash, anchors[].
     (pubkeySig, pubkeyFingerprint, policyMode are optional pre-v2.)

2. Rebuild the Merkle root from anchors.map(a => a.leafHash).
   Fail with "merkle_root_mismatch" if it differs.

3. (Hosted only — requires the signingSecret) re-derive the HMAC over the
   canonical payload. Fail with "hmac_signature_mismatch" on mismatch.

4. If pubkeySig is present:
     a. Pick a trusted public key. Sources, in priority order:
          i.   caller-supplied --pubkey
          ii.  bundle.pvaio.publicKeyPem
          iii. issuer's /.well-known/pvaio-keys.json (must fingerprint-match)
     b. Verify pubkeySig against the canonical payload. Mismatch → invalid.
     c. No fingerprint-matching key found → verdict "key_unknown".

5. If originalText is provided (optional but strongly recommended):
     For each anchor, recompute leafHash using the formula from §3.
     Any mismatch → "anchor_replay_mismatch".

6. Emit a verdict:
     valid        — all applicable checks passed
     invalid      — one or more checks failed
     key_unknown  — no fingerprint-matching key available
     expired      — (reserved; future time-bound ledgers)

08. Offline CLI

We ship pvaio-verify, a dependency-free Node CLI. An air-gapped laptop with only the issuer's public key can verify forever:

npm install -g pvaio-verify
pvaio-verify ./alice-resume.hrjson.json
#   ✓ PVAIO valid
#     ✓ Merkle root
#     ✓ Ed25519 signature
#     ✓ Optimized-text hash
#     ✓ Policy = strict
#     ✓ Fabricated = 0
#     ✓ Fabrication-free proof: ok (42 witnesses)
#     engine 2.7.0 · fingerprint a1b2c3d4e5f67890 · issued 2026-04-27T08:12:03Z

The CLI skips the HMAC step entirely — it has no access to the signing secret and doesn't need one. Merkle + Ed25519 + replay are sufficient to catch any tampering.

09. Public API

A reference verifier is operated at POST /api/pvaio/verify. 60 requests per 15 minutes per IP free, 1000/day per API token. Full request/response contract and curl examples live in the developer reference.

The verifier is a neutral public good. Third parties MAY run their own — the protocol does not privilege this deployment.

10. Version

This document describes PVAIO v1.0 (of the protocol), reference-implemented by PVAIO engine v2.7.x. Engine versions bump freely; protocol version bumps only on wire-breaking changes. Ledgers carry the engine version inline so older bundles stay verifiable forever.

Canonical spec URL: /protocol · Schema: /.well-known/pvaio-hrjson.schema.json · Keyset: /.well-known/pvaio-keys.json

11. Revocation

Signing proves a ledger was minted correctly. It does not say the ledger should still be trusted TODAY. PVAIO v2.6 adds a CRL-style revocation layer so candidates can pull badges, operators can react to key compromise, and verifiers can reach a fail-closed verdict years after the signing event.

The canonical revocation feed is served at the well-known URL /.well-known/pvaio-revocation-list.json. Every verifier — our CLI, the Chrome extension, federated third parties — can poll it on a cheap CDN-friendly cadence and cache on the 60-second edge TTL.

GET /.well-known/pvaio-revocation-list.json
-> {
  "generatedAt": "2026-04-27T02:00:00.000Z",
  "entries": [
    {
      "merkleRoot": "<64-hex>",
      "revokedAt": "2026-04-25T10:12:44Z",
      "reason": "candidate_request"
    }
  ]
}

Revocation reasons are a closed set: candidate_request, key_compromise, superseded, corrupted, other. Verifiers that care about provenance (e.g. background-check vendors) may choose to accept superseded as "replaced by a newer badge" rather than a hard fail; all other reasons SHOULD be treated as invalid.

To implement a revocation-aware verifier:

1. Fetch the feed once per minute (or on cache miss).
2. After running the Merkle + HMAC + Ed25519 checks from Section 7,
   look up ledger.merkleRoot in feed.entries.
3. If found: verdict = "invalid", reason = "revoked".
4. If not found AND feed was fetched successfully:
   checks.revocation = "not_revoked".
5. If the feed was unreachable: checks.revocation = "unknown".
   Fail-open is a policy choice; we recommend surfacing the
   gap rather than rendering a false-positive "trusted".

The first-party endpoint POST /api/pvaio/revocation lets the ledger owner (or an ATS holding a pvaio_live_ API token) revoke a root by reason. The endpoint is idempotent: a second revocation of the same root returns the original timestamp.

Anti-replay nonces. v2.6 ledgers MAY carry an optional { nonce, validUntil } block on the envelope. When present, the nonce is appended to the canonical payload (so any mutation voids the signature) AND recorded server-side. If the same nonce is ever seen against a different Merkle root, the verifier surfaces replay_nonce = "conflict" and returns verdict = "invalid". Ledgers without the block verify exactly as they did in v2.5 — the layer is fully backwards-compatible.

# CLI: poll the feed during offline verification
$ pvaio-verify resume.hrjson \
    --check-revocation https://example.com/.well-known/pvaio-revocation-list.json

12. Fabrication-free transparency claims

A PVAIO ledger reveals every anchor's kind — including which spans were flagged fabricated — to any party holding the envelope. That is perfect for strict-mode recruiting flows, but it forces the candidate to disclose everything the classifier saw. PVAIO v2.7 adds an optional fabrication-free proof that cryptographically asserts "no anchor in this ledger is fabricated" with per-anchor commitments instead of raw kind labels. The proof binds to the signed canonical payload, so no third party can strip or substitute it without voiding the signature.

Construction. For each anchor at position i:

s_i  = 32 random bytes                          (per-anchor salt)
kc_i = sha256(kind_i "|" hex(s_i) "|" i)         (kind commitment)
leaf_i = sha256("kind-commitment:" || kc_i)      (domain-separated leaf)

claimRoot = merkle(leaf_0, leaf_1, …, leaf_{n-1})
                                                 (same construction as §4:
                                                  bitcoin-style, duplicate
                                                  last on odd tail)

The envelope carries fabricatedFreeProof:

{
  "claimRoot":  "<64-hex>",
  "algorithm":  "pvaio-kind-merkle-v1",
  "assertion":  "none-fabricated",
  "witnesses": [
    {
      "pos": 0,
      "kind": "exact" | "fuzzy" | "semantic",
      "salt": "<64-hex>",
      "kindCommitment": "<64-hex>",
      "pathHashes":     ["<hex>", "<hex>", …],
      "pathDirections": ["R", "L", …]
    },
    …
  ]
}

Binding to the ledger. When a v2.7 ledger carries a proof, the canonical payload gets a new trailing block:

engineVersion | resumeHash | jobHash | merkleRoot
  | policyHash?
  | "pvaio26:" issuedAt ":" nonce ":" validUntil     (only if v2.6 fields present)
  | "pvaio27:" claimRoot                              (only if fabricatedFreeProof present)

Both the HMAC and the Ed25519 signatures cover the full string. Any mutation to claimRoot (or to any witness whose commitment no longer reproduces) invalidates the signature chain. Omitting the block entirely yields bytes byte-identical to a v2.6 envelope, so pre-v2.7 ledgers verify unchanged.

Verification procedure. Given a proof:

1. Check proof.algorithm == "pvaio-kind-merkle-v1" and
   proof.assertion == "none-fabricated". Reject on mismatch.
2. For each witness w:
     a. Reject if w.kind not in {exact, fuzzy, semantic}.
     b. Reject on duplicate w.pos.
     c. Recompute kc' = sha256(w.kind "|" w.salt "|" w.pos)
        and assert kc' == w.kindCommitment.
     d. leaf = sha256("kind-commitment:" || w.kindCommitment).
     e. Walk (leaf, w.pathHashes, w.pathDirections) back up;
        assert the walk yields proof.claimRoot.
3. Assert the ledger's canonical payload INCLUDES
   "pvaio27:" + proof.claimRoot — i.e. the signature binds it.
4. The verdict's checks.fabricated_free = "ok" | "fail" | "absent".
   "absent" is NOT a failure unless --require-fabricated-free is set
   (CLI) or the verifier was constructed with requireFabricatedFree.

What this proof does NOT claim. The construction is honest-issuer-bounded: a malicious issuer who labels a fabricated span as semantic at mint time will pass the check. The scheme only proves that the issuer committed to non-fabricated kinds under a binding salt, not that the classifier was correct. True zero-knowledge (where the verifier learns non-fabricated without learning which of exact / fuzzy / semantic, and without access to salts) requires a SNARK; see the v3.x Halo2 section below. Anchor count is also not hidden — witnesses.length is the ledger's anchor count.

Relationship to future Halo2 circuit. When PVAIO v3.x ships the Halo2 / groth16 circuit, this scheme is not thrown away. The existing kindCommitment values slot in unchanged as circuit witnesses, and the canonical-payload block re-uses the pvaio27: prefix so earlier v2.7 ledgers can be proven against the new circuit without re-issuance. Our pledge to candidates: once the circuit lands, their v2.7 proofs can be upgraded to full zero-knowledge without invalidating a single signature.

# programmatic verification
POST /api/pvaio/verify?requireFabricatedFree=1
Content-Type: application/json

{ "bundle": { "pvaio": { "ledger": { … } }, "optimizedText": "…" } }

# → 200 OK
# {
#   "ok": true, "verdict": "valid",
#   "checks": { …, "fabricated_free": "ok" },
#   "merkleRoot": "…", "signedBy": "…"
# }

# offline CLI
$ pvaio-verify resume.hrjson --require-fabricated-free