Skip to content

Bureau — Blue Team (defensive)

Citizen-Ledger

Personal records are typically held by the institutions that originated them – hospitals, employers, credit bureaus – with the individual as the subject rather than the cryptographic root. Citizen-Ledger inverts the polarity: the individual signs the root of each personal record and institutions countersign with explicit consent labels.

Posture: 🔵 Blue Team (defensive)   ·   Status: alpha (moonshot)

What it does

Personal records currently reside wherever the originating institution chose to store them. The subject is rarely the cryptographic root, and there is typically no signed contract that specifies precisely how the institution may share the record.

Citizen-Ledger inverts this arrangement. The individual signs each personal record (a hospital visit, an employment date, a court filing, a financial transaction, an education credential) with their own key, producing the root. Institutions then countersign the records they hold, attaching an explicit consent label – internal:only, aggregate:anonymized, share:specific, or none. Sharing outside the agreed label produces a verifiable contract violation.

Signed bodies never carry the actual record content, the subject's name, or any protected health information. Only sha256 digests are published.

Who would use it

  • A patient who wants to revoke a hospital's right to share their chart with a research vendor – and prove the hospital kept sharing it after revocation.
  • A union member whose employer disputes their tenure dates; the citizen's signed employment record predates and out-attests the employer's HR system.
  • An identity-theft victim who can show that a financial record claimed by a bank was never co-signed by them – the bank holds a fabrication.
  • A privacy-rights NGO (EFF, Privacy International, NOYB) building case dossiers against institutions that share data outside signed envelopes.
  • A lawyer in a HIPAA / GDPR action who needs cryptographic proof of what consent the citizen actually granted at what time.

What you'll need

  • The Pluck CLI installed (npm i -g @sizls/pluck-cli).
  • An operator key (the demo generates one for you).
  • For real deployment: institutions on the other side willing to co-sign – that's the hard part, and it's a social problem the program does not solve. Today, Citizen-Ledger ships the signed-chain shape; getting a hospital or bank to honor the chain is a separate fight.

Step-by-step

Shell
pluck bureau citizen-ledger demo

The demo wires up one citizen with four records (health, employment, legal, financial) and four institutional co-signs. One institution declared internal:only consent then is observed sharing externally. One court co-signed a record the citizen never actually signed. A bank co-signed a financial record after the citizen revoked it. The fourth co-sign is clean.

citizen-ledger/demo: ingesting 4 self-signed records + 4 institutional counter-signs (1 violation, 1 missing-counter-sign, 1 post-revoke-use, 1 clean) + 1 citizen-issued revocation -> 3 CitizenLedgerProofs.
[Bureau/CITIZEN-LEDGER] proof=78a8c3f9… kind=violation-of-self-sovereignty
[Bureau/CITIZEN-LEDGER] proof=fa046d81… kind=missing-counter-sign
[Bureau/CITIZEN-LEDGER] proof=4b5c8689… kind=post-revoke-use

Production CLI (capture to sign your own record, revoke to issue a revocation, verify to check a published chain) lands in a follow-up.

Run it yourself

Drop this into a Node 18+ project (npm install @sizls/pluck-bureau-citizen-ledger @sizls/pluck-bureau-core tsx):

TypeScript
// index.ts
import { createHash } from "node:crypto";
import {
  createCitizenLedgerSystem,
  fingerprintPrivateKey,
  signCanonicalBody,
} from "@sizls/pluck-bureau-citizen-ledger";
import { generateOperatorKey } from "@sizls/pluck-bureau-core";

const sha256 = (s: string) => createHash("sha256").update(s).digest("hex");
const flush = (n = 60) => new Promise<void>((r) => { let i = 0; const tick = () => (++i >= n ? r() : setImmediate(tick)); setImmediate(tick); });

async function main() {
  const op = generateOperatorKey();
  const citizen = generateOperatorKey();
  const citizenFp = fingerprintPrivateKey(citizen.privateKeyPem);
  const court = generateOperatorKey();
  const courtFp = fingerprintPrivateKey(court.privateKeyPem);

  const system = createCitizenLedgerSystem({
    signingKey: op.privateKeyPem,
    disablePausePoll: true,
    disableLogging: true,
  });

  // The court claims a record on file that the citizen never signed -> missing-counter-sign.
  const fabricatedDigest = sha256("record:legal:fabricated");
  const citizenIdHash = sha256("citizen-salt:CITIZEN-001");
  const csBody = {
    schemaVersion: 1 as const,
    recordDigest: fabricatedDigest,
    citizenIdHash,
    institutionFingerprint: courtFp,
    disclosureConsent: "internal:only" as const,
    observedAt: "2026-04-25T10:00:00.000Z",
  };
  const counterSignId = sha256(JSON.stringify(csBody));
  const { signature } = signCanonicalBody({ ...csBody, counterSignId }, court.privateKeyPem);
  void citizenFp; // citizen key is provisioned for completeness; this scenario fires without a citizen-side record.

  try {
    system.observeCounterSign({ ...csBody, counterSignId, signature });
    await flush();
    for (const p of system.facts.proofs()) {
      console.log(`proof kind=${p.kind} id=${p.proofId.slice(0, 16)}`);
    }
  } finally {
    await system.shutdown();
  }
}

main().catch((err) => { console.error(err); process.exit(1); });

Run with tsx index.ts. Expected output:

proof kind=missing-counter-sign id=…

▶ Open in StackBlitz – runs in your browser, no install required.

What you get

Three classes of red-team proof when something breaks:

  • violation-of-self-sovereignty – an institution shared outside the citizen's signed consent label.
  • missing-counter-sign – an institution claims to hold a record the citizen never signed (a fabrication).
  • post-revoke-use – an institution accessed the record after the citizen revoked.

Each proof is an Ed25519-signed envelope, anchored to Sigstore's public Rekor log, that a court, a regulator, or a journalist can independently verify with cosign verify-blob – no Pluck-Inc trust required.

What it can't do

  • Cannot force institutions to participate. Citizen-Ledger is a primitive. If your hospital refuses to co-sign anything, the protocol cannot compel them. The lever is regulatory: HIPAA/GDPR enforcers demanding audit trails, lawsuits demanding disclosure, market pressure from privacy-conscious patients.
  • Real institutional bridges are deferred – there is no plug-in for Epic, Workday, or Equifax today. Plumbing those is per-vendor work.
  • Real Sigstore Rekor notarization is stubbed in the alpha – the demo writes notarized envelopes locally; production will plumb the real Rekor client.
  • Consent vocabulary is small – alpha ships four labels (none | aggregate:anonymized | share:specific | internal:only); production needs HIPAA's full Treatment-Payment-Operations matrix and GDPR's lawful-basis matrix.
  • No protection from key loss. If you lose your signing key without rotating, you lose the ability to revoke. Pair with ROTATE.

A real-world example

In June 2027, a patient at a Boston hospital signs her primary-care visit record with her Citizen-Ledger key. The hospital countersigns with the consent label internal:only. Six months later, a third-party data broker appears in a credit-decision pipeline carrying her diagnosis history. Her attorney retrieves the broker's source chain and finds the hospital's countersignature with the internal:only consent label, which does not authorize external sharing. Citizen-Ledger emits a violation-of-self-sovereignty proof. The signed record specifies the exact label the hospital agreed to, supporting a HIPAA complaint and a contract claim.


For developers

Predicate URIs

URIWhat it attests
https://pluck.run/CitizenLedger.SelfRecord/v1Citizen C signed a record of kind K with digest D at time T (root).
https://pluck.run/CitizenLedger.CounterSign/v1Institution I co-signed citizen-signed record D, claiming consent label X.
https://pluck.run/CitizenLedger.Revocation/v1Citizen C revoked record D (or all records) at time T.
https://pluck.run/CitizenLedger.Proof/v1Class: violation-of-self-sovereignty | missing-counter-sign | post-revoke-use.

The signed body never carries raw citizen identity, raw record content, or PHI. Only the citizen-id-hash (sha256 of a salted citizen id), record digest, record kind, and signing-key fingerprints appear.

Programs composed

  • ROTATE – citizen-key rotation and revocation routing.
  • Whistle – anonymous channel for institutional cover-up disclosure.
  • Oath – institutional self-sovereignty commitments.
  • Pluck core's DSSE in-toto envelopes + Sigstore Rekor client.
  • Directive's facts/constraints/resolvers.

Threat model + adversary

A coordinated institution that holds your record, refuses to co-sign anything, and dumps it in a breach is not stopped by Citizen-Ledger today – the protocol catches signed contradictions, not silent exfiltration. The defense is regulatory pressure to require co-signing.

What's stubbed (alpha – moonshot)

  • Real institutional cooperation rails (Epic, Workday, equivalents) deferred.
  • dsseSign / notarizeAttestation Rekor integration stubbed.
  • Full ROTATE compromise-recovery integration deferred beyond simple revocation.
  • Disclosure-consent vocabulary expansion deferred.

Verify a published cassette

Shell
pluck bureau verify <bundle-dir>
cosign verify-blob --key <pubkey.pem> --signature <sig> \
  --type https://pluck.run/CitizenLedger.Proof/v1 <body.json>

pluck bureau verify checks Ed25519 signatures, Rekor inclusion, citizen / institution fingerprint distinctness, schema bounds, consent-label enum membership, and revocation-time-ordering arithmetic.

See also

Edit this page on GitHub
Previous
Trial-Seal
Next
Gossip

Ready to build?

Install Pluck and follow the Quick Start guide to wire MCP-first data pipelines into your agents and fleets in minutes.

Get started →