-
Notifications
You must be signed in to change notification settings - Fork 0
Data Agency
The conversation belongs to the person having it. This page is what that means concretely.
persist defaults to off.
<!-- nothing is written anywhere -->
<machvive-chat-syncopation-services>
<!-- conversations go to IndexedDB, in this browser -->
<machvive-chat-syncopation-services persist>Storing someone's conversation is a decision a page has to make deliberately, not a
default it inherits by installing a component. Two tests pin this from both
directions — that persist writes through, and that nothing is stored without it —
because a default-on cache would quietly retain everything anyone typed, which is
the opposite of the stated promise.
IndexedDB, database machvive-chat-syncopation, store conversations, keyed by
conversation id. In the user's own browser and nowhere else. No network request is
involved, because no module makes network requests.
A transcript grows without bound and localStorage is a synchronous ~5 MB cliff — it fails at exactly the point the conversation got interesting, and it blocks the main thread while it works.
The cache degrades to memory when IndexedDB is unavailable (a private window, storage disabled by policy, a plain Node process) rather than throwing. History is lost; the surface keeps working. Losing history must never break the conversation in progress.
Writes are fire-and-forget. Storage failing is never allowed to interrupt a turn.
<machvive-chat-syncopation-history></machvive-chat-syncopation-history>Three operations, which together are what "agency" has to mean if it means anything:
| Control | Does |
|---|---|
| Open | Replays a stored conversation into the live one |
| Export | Hands over a JSON file. No upload, no endpoint |
| Delete | Removes it — the stored row and the in-memory copy |
Plus Export all and Delete all in the header.
Cache#remove and Cache#clear drop the IndexedDB row and the memory copy. A
"clear history" that only hides rows is a lie, and this is the one place in the
collection where that distinction carries real weight. A test asserts that a removed
conversation is unreadable afterwards — including from memory, which is the half
that is easy to forget.
history.addEventListener('history-export', ({ detail }) => {
// detail.json is the same string the user is downloading
});
const json = await history.export(); // all of them
const one = await history.export('c-abc'); // just oneThe method returns the JSON and dispatches the event, then offers a download. There is no endpoint and no telemetry hook — the user's data goes to the user.
const { cache } = services;
await cache.list(); // [{ id, records }]
await cache.get(id);
await cache.remove(id);
await cache.clear();
await cache.put(conversation.toJSON());
cache.available; // false when IndexedDB is unusableIf you build a settings screen instead of using the history component, keep all three operations. Export without delete is a photocopier; delete without export loses something the user may have wanted.
Dictation is usually not local. SpeechRecognition in most browsers sends audio
to a vendor service. The voice component cannot change that, but you can say so
where you put the microphone button — particularly if the rest of your surface runs
a local model and is otherwise private.
A local model means the conversation never leaves the device. With
transport="local", there is no server-side copy to subpoena, breach or retain.
That is a stronger privacy claim than any hosted provider can make, and it is worth
stating plainly rather than burying.
Clearing site data clears this. IndexedDB goes when the user clears site data, and may be evicted under storage pressure. Persistence here is a convenience, not a system of record. If a conversation matters to your business, your server should have its own copy — with its own consent, its own retention policy, and its own deletion path.
- No network requests. A test fails if any module gains one.
- No runtime dependencies, so no transitive supply chain. A test asserts it, in the manifest and the lock file.
- No telemetry. Importing a module emits nothing.
- No
window.dataLayer, no analytics globals, no beacons. - No credential of any kind.
Next: Design Decisions.