This repository was archived by the owner on Jun 29, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathright-to-be-forgotten.ts
More file actions
137 lines (117 loc) Β· 4.97 KB
/
Copy pathright-to-be-forgotten.ts
File metadata and controls
137 lines (117 loc) Β· 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* Right to be Forgotten Demo
*
* Demonstrates how to handle data deletion on an immutable DAG.
*
* Strategies:
* 1. Tombstoning: Mark as deleted (Client-side filtering).
* 2. Crypto-shredding: Delete the decryption key.
* 3. Off-chain: Delete the source, leave the hash.
*/
import {
generateZone,
createAttestation,
sha256Hex,
GLR,
computeCanonId,
type AttestationInput,
type SubjectHash,
type UnixTimestamp,
type HashHex
} from '@glogos/core';
const SOCIAL_POST_CANON = computeCanonId('opt:social:post:1.0');
const TOMBSTONE_CANON = computeCanonId('opt:social:tombstone:1.0');
const ENCRYPTED_CANON = computeCanonId('opt:data:encrypted:1.0');
export async function runRightToBeForgottenDemo() {
console.log('='.repeat(80));
console.log('RIGHT TO BE FORGOTTEN DEMO');
console.log('Reconciling Immutability with Privacy');
console.log('='.repeat(80));
const alice = await generateZone();
console.log(` π€ Alice: ${alice.id.substring(0, 16)}...`);
// ============================================================================
// STRATEGY 1: TOMBSTONING (Social Deletion)
// ============================================================================
console.log('\n1. Strategy: Tombstoning (Soft Delete)');
console.log(' Alice posts something regrettable, then "deletes" it.');
// 1a. The Regrettable Post
const postPayload = { content: 'I hate my boss!', mood: 'angry' };
const postInput: AttestationInput = {
zone: alice.id,
subject: sha256Hex(JSON.stringify(postPayload)) as SubjectHash,
canon: SOCIAL_POST_CANON,
time: Math.floor(Date.now() / 1000) as unknown as UnixTimestamp,
refs: [GLR as HashHex]
};
const { attestation: post } = await createAttestation(postInput, alice.privateKey);
console.log(` π’ Post created: "${postPayload.content}" (ID: ${post.id.substring(0, 8)}...)`);
// 1b. The Tombstone (Deletion Request)
const tombstonePayload = {
action: 'delete',
target: post.id,
reason: 'GDPR Request'
};
const tombstoneInput: AttestationInput = {
zone: alice.id,
subject: sha256Hex(JSON.stringify(tombstonePayload)) as SubjectHash,
canon: TOMBSTONE_CANON,
time: (Math.floor(Date.now() / 1000) + 100) as unknown as UnixTimestamp,
refs: [post.id as HashHex] // Must reference the target
};
const { attestation: tombstone } = await createAttestation(tombstoneInput, alice.privateKey);
console.log(` πͺ¦ Tombstone created for ID: ${post.id.substring(0, 8)}...`);
// 1c. Client View
console.log(' π Client View:');
const history = [post, tombstone];
const deletedIds = new Set(
history
.filter((a) => a.canon === TOMBSTONE_CANON)
.map((a) => history.find((target) => target.id === a.refs[0])?.id)
);
const visiblePosts = history.filter(
(a) => a.canon === SOCIAL_POST_CANON && !deletedIds.has(a.id)
);
if (visiblePosts.length === 0) {
console.log(' β
Post is HIDDEN from UI (Filtered by Tombstone)');
} else {
console.log(' β Post is still visible!');
}
// ============================================================================
// STRATEGY 2: CRYPTO-SHREDDING (Hard Delete)
// ============================================================================
console.log('\n2. Strategy: Crypto-shredding (Hard Delete)');
console.log(' Alice encrypts data. To delete, she destroys the key.');
// 2a. Encrypt Data
const sensitiveData = 'My Medical Records: Healthy';
const encryptionKey = 'correct-horse-battery-staple'; // In reality, a random 256-bit key
console.log(` π Encrypting data: "${sensitiveData}"`);
console.log(` π Key generated (simulated): "${encryptionKey.substring(0, 8)}..."`);
// Simulate encryption: AES(data, key)
const cipherText = Buffer.from(`ENCRYPTED[${sensitiveData}]WITH[${encryptionKey}]`).toString(
'base64'
);
const encryptedInput: AttestationInput = {
zone: alice.id,
subject: sha256Hex(JSON.stringify({ cipherText })) as SubjectHash,
canon: ENCRYPTED_CANON,
time: (Math.floor(Date.now() / 1000) + 200) as unknown as UnixTimestamp,
refs: [GLR as HashHex]
};
const { attestation: encryptedPost } = await createAttestation(encryptedInput, alice.privateKey);
console.log(` cw Encrypted Post on DAG: ${encryptedPost.id.substring(0, 8)}...`);
console.log(` Content: "${cipherText.substring(0, 20)}..."`);
// 2b. Access (With Key)
console.log(' π Access with Key: Data is readable.');
// 2c. "Delete" (Destroy Key)
console.log(' π₯ Action: Alice destroys the Encryption Key.');
const destroyedKey = null;
// 2d. Access (Without Key)
console.log(' π Access without Key:');
if (!destroyedKey) {
console.log(' β
Data is mathematically unrecoverable (Indistinguishable from noise).');
console.log(' The DAG entry remains, but the *information* is gone.');
}
console.log('\nβ
Right to be Forgotten Demo completed!');
console.log('='.repeat(80));
}
runRightToBeForgottenDemo().catch(console.error);