Appearance
Erase an identity from the audit log
Every change you made in the earlier lessons landed in a per-Domain, hash-chained audit log — a tamper-evident trail you can recompute byte for byte. Now hold that next to a different obligation: under GDPR Article 17, a person can demand that their personal data be deleted. A tamper-evident, append-only log and a deletion request look like a contradiction. How do you remove someone from a chain whose whole point is that nothing in it can change?
This capstone resolves the contradiction. You will honour a right-to-erasure request with plexctl audit erase-identity, watch the identity's pseudonym mapping disappear, and then prove the two things that make the design work: the hash-chain still verifies end to end, and the erasure itself is recorded as a new audit entry. By the end you will understand why the chain only ever stored a pseudonym — and how that one decision lets plexsphere be both forensically sound and legally erasable at the same time.
This lesson takes about fifteen minutes.
Before you start
This is a capstone: it assumes you have worked through Explore your first Domain and Build in your first Domain, so the Acme Corp Domain already carries a hash-chained audit log written by your own actions. You need a running, logged-in stack from Set up your local plexsphere and jq on your $PATH.
Recreate the shell environment and capture the two ids this lesson works with — the Domain you are erasing inside, and the identity whose data is being erased:
bash
export PATH="$PWD/bin:$PATH"
export PLEXSPHERE_URL=http://localhost:8080
DOMAIN_ID=$(kubectl exec statefulset/postgres -- \
env PGPASSWORD=plexsphere psql -U plexsphere -d plexsphere -tAc \
"SELECT id FROM plexsphere.domains WHERE slug='acme-corp'")
USER_ID=$(plexctl whoami --output json | jq -r '.subject')$USER_ID is the seeded admin@example.com user — the principal you have been acting as. In this demo that is the only identity that has actually done anything in Acme Corp (the user you invited in the build lesson never accepted, so they never acted and left no trace). So the admin stands in for any data subject who exercises their right to erasure: it is the one identity with personal data in this Domain's audit log to erase.
Erasing an identity's audit mapping does not delete the Identity or revoke its access. It removes only the audit log's ability to map a pseudonym back to a person. You stay logged in and keep operating throughout this lesson.
Step 1 — See the personal data the log holds
The audit chain never stores who you are in plaintext. It stores a per-Domain pseudonym — a one-way hash of your subject — and keeps the plaintext link in a separate table, audit_subject_pii. That sibling table is the only thing that can turn a pseudonym back into a person, and it is exactly what a right-to-erasure request targets.
First make sure there is something to erase: run any audited action so the log records you, then look at the pseudonym mapping it created.
bash
plexctl identity list --domain "$DOMAIN_ID" >/dev/null
kubectl exec statefulset/postgres -- \
env PGPASSWORD=plexsphere psql -U plexsphere -d plexsphere -c \
"SELECT subject_plaintext, encode(subject_pseudonym,'hex') AS pseudonym
FROM plexsphere.audit_subject_pii WHERE domain_id='$DOMAIN_ID'"text
subject_plaintext | pseudonym
------------------------------+------------------------------------------------------------------
user:<seed-user-uuid> | <pseudonym-hex>
(1 row)There it is: the chain holds <pseudonym-hex>, and this row is the only place that ties that pseudonym to the real subject user:<seed-user-uuid>. Delete this row and the pseudonym on every chain entry becomes an orphan — present, but no longer resolvable to a person. Capture the pseudonym so you can follow it through the erasure:
bash
PSEUDONYM=$(kubectl exec statefulset/postgres -- \
env PGPASSWORD=plexsphere psql -U plexsphere -d plexsphere -tAc \
"SELECT encode(subject_pseudonym,'hex') FROM plexsphere.audit_subject_pii
WHERE domain_id='$DOMAIN_ID' LIMIT 1")Step 2 — Confirm the chain is intact before you erase
Establish the baseline you are about to defend. audit verify recomputes the whole hash-chain and tells you whether a single byte has shifted:
bash
plexctl audit verify --domain "$DOMAIN_ID"text
VALID SEGMENT_FROM SEGMENT_TO DIVERGENT_SEQ EXPECTED_HASH OBSERVED_HASH
valid=true 1 <head-seq> <unknown>valid=true from seq 1 to the head: the chain recomputes cleanly. Hold on to that result — the whole point of this lesson is that it stays true after you delete the person's data.
Step 3 — Honour the right-to-erasure request
Now erase the identity's mapping. The command is deliberately blunt about what it does: it is irreversible, so it refuses to run without an explicit --confirm.
bash
plexctl audit erase-identity \
--domain "$DOMAIN_ID" \
--identity "$USER_ID" \
--confirmtext
SUBJECT_PSEUDONYM ERASED_AT
<pseudonym-hex> <timestamp>The response echoes the pseudonym that was erased — the same <pseudonym-hex> you captured in Step 1 — and the instant it happened. It never echoes the identity back: a transcript of the erasure must not become a way to re-derive the mapping you just destroyed. The pseudonym is safe to print precisely because, once the next step finishes, it no longer points at anyone.
--confirmis mandatory and--yesdoes not substitute for it. Dropping theaudit_subject_piirow is forensically irreversible — the pseudonym-to-person link is gone for good — so the command makes you type the word--confirmrather than letting destructive muscle memory carry an ordinary--yesinto an unrecoverable operation.
Step 4 — Prove the mapping is gone but the chain survives
Three checks close the loop — and together they are the payoff of the lesson.
First, the personal data is gone. Re-read the sibling table:
bash
kubectl exec statefulset/postgres -- \
env PGPASSWORD=plexsphere psql -U plexsphere -d plexsphere -c \
"SELECT count(*) FROM plexsphere.audit_subject_pii
WHERE domain_id='$DOMAIN_ID' AND encode(subject_pseudonym,'hex')='$PSEUDONYM'"text
count
-------
0
(1 row)Zero rows. The de-pseudonymisation key for that subject no longer exists anywhere in the system — the Article 17 obligation is met.
Second, the chain still verifies. Run the exact same command as Step 2:
bash
plexctl audit verify --domain "$DOMAIN_ID"text
VALID SEGMENT_FROM SEGMENT_TO DIVERGENT_SEQ EXPECTED_HASH OBSERVED_HASH
valid=true 1 <head-seq> <unknown>Still valid=true. You deleted a person's data and the tamper-evidence property did not even flinch — because the bytes the chain hashes never included that data in the first place.
Third, the erasure is itself audited. Read the tail of the log — the last two rows are the identity.list from Step 1 and the erasure you just ran:
bash
plexctl audit entries list --domain "$DOMAIN_ID" --all | tail -2text
SEQ OCCURRED_AT REASON RELATION OBJECT_TYPE OBJECT_ID CORRELATION_ID
<seq> <timestamp> granted identity.list domain <acme-corp-uuid> <correlation-id>
<seq> <timestamp> granted audit.erase-identity audit-subject <pseudonym-hex>A new audit.erase-identity entry sits at the head, recorded like any other authorized act, with REASON = granted. Its OBJECT_ID is the very pseudonym you erased — so the audit trail attests to the deletion by pseudonym, never by re-introducing the plaintext it just removed. Erasing the data and recording the erasure do not conflict: the record of the deletion is itself pseudonymous.
Step 5 — Why it does not break
Look at what each surface holds, and the apparent contradiction dissolves:
- The chain rows carry the pseudonym
<pseudonym-hex>and the hash that seals each row. Erasure never touches them, soaudit verifyrecomputes exactly as before. Forensic linkability within the chain survives — every row that subject wrote still groups under the same pseudonym. - The
audit_subject_piirow was the one place that turned that pseudonym back into a person. Erasure dropped it. The link out of the chain — to the real human — is gone.
That split is the whole design, chosen from the start. Had the chain stored the subject in plaintext, erasure would have to rewrite the bytes that produced each entry_hash, snapping the chain. Because the chain only ever stored a pseudonym, erasure is a single DELETE on a sibling table — and the tamper-evident trail stays mathematically valid while the person walks away from it.
What you learned
- Tamper-evidence and the right to erasure are not in conflict. The chain hashes a pseudonym, never plaintext, so deleting a person's data leaves every hash intact.
- Erasure targets one sibling row.
audit erase-identitydrops theaudit_subject_piimapping for an identity; the chain rows that reference the pseudonym are never rewritten. - The operation is irreversible by design. It demands an explicit
--confirm, and the response echoes only the pseudonym — never the identity — so the act of erasing cannot leak what it erased. - The erasure is itself audited. A new
audit.erase-identityentry, addressed by pseudonym, joins the chain — the deletion is recorded without un-deleting anything. audit verifyis the proof.valid=truebefore and after is what lets you honour a deletion request and still hand an auditor a chain that recomputes end to end.
Where to go next
- You have a real job to do — the query the audit log how-to covers the operational read-and-verify runbook.
- You want the exact contract — the
plexctl auditreference documents every subcommand, the--confirmgate, and the verify exit-code rule. - You want to understand why the chain is shaped this way — the retention and right to erasure explanation walks the pseudonymise-from-inception design and the alternatives it was chosen against.
- Done with the stack, or want a clean slate — Tear down your local plexsphere removes the cluster and returns the Domain to its seeded state.