feat(client): OFF-by-default document access log, prefixed by active identity
Observability probe for the shared-wallet isolation footgun: on one physical
wallet several virtual identities coexist, and a read must never surface a doc
scoped to another identity. When it does (B reading A's doc), the leak is
invisible in the data — it looks like a normal read. This makes it VISIBLE.
Every real read/write is logged, prefixed by the ACTIVE virtual identity
(getCurrentUser → the account the op is scoped under, NOT the constant shared
physical wallet id). Reads append the row count — a strong leak signal:
[urn:festipod:user:bob] READ did:ng:o:docA (readDoc) → 3 rows
Instrumented at the LOW common point in docs.ts: every read routes through
sparqlQuery, every write through sparqlUpdate, container creation through
docCreate. Callers pass a semantic label (readDoc|readUnion|listMyEntityDocs|
writeEntity|deposit|…) that is a lib-internal probe param, NOT forwarded to the
real `ng` (preserves docs.test.ts exact-forwarding assertions).
OFF by default → one boolean read on the hot path, zero output. On via
configure({ debugAccessLog: true }) or env NG_EVENTUALLY_ACCESS_LOG=1 (no code
change). Polyfill-era; removed at the real multi-store migration.
tsc --noEmit: 0 errors. bun test: 91 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -142,7 +142,7 @@ export async function post(targetInbox: Nuri, opts: PostOptions): Promise<void>
|
||||
<${P.payload}> "${payloadLiteral}" ;
|
||||
<${P.ts}> "${ts}"${fromTriple} .
|
||||
}`;
|
||||
await sparqlUpdate(sid, update, targetInbox);
|
||||
await sparqlUpdate(sid, update, targetInbox, "deposit");
|
||||
}
|
||||
|
||||
// --- read --------------------------------------------------------------
|
||||
@@ -166,7 +166,7 @@ export async function read(targetInbox: Nuri): Promise<Deposit[]> {
|
||||
<${P.ts}> ?ts .
|
||||
OPTIONAL { ?d <${P.from}> ?from }
|
||||
}`;
|
||||
const result = await sparqlQuery(sid, query, undefined, targetInbox);
|
||||
const result = await sparqlQuery(sid, query, undefined, targetInbox, "inboxRead");
|
||||
const deposits: Deposit[] = [];
|
||||
for (const row of readBindings(result)) {
|
||||
const rawPayload = row.payload?.value ?? "null";
|
||||
|
||||
Reference in New Issue
Block a user