Eidetium
Summary
Eidetium is a hosted memory service that makes AI agents follow the owner's taste and decisions, and keeps every agent transcript in one searchable cloud. "The owner" throughout is the person whose rulings agents obey.
It is two products sharing one plugin:
- Precedent engine. One tool agents use to remember the owner's taste and decisions. Each precedent is one fact tied to the owner's exact words. A newer ruling replaces an older one by an explicit subject key, never by similarity. Ranking decays slowly and rises slowly with citations.
- Transcript cloud. One cloud for every agent transcript, stored raw, so a user with several devices searches one corpus. The same corpus is the single source for usage reporting, compatible with ccusage.
Eidetium is a managed, hosted service. Its source is private, and it is not packaged or documented for self-hosting.
Problem
Agents forget what the owner already decided, and current memory tools corrupt what they do remember. The failures below come from a founding user's own transcripts with Supermemory; section 14 maps each to its fix.
- Meaning drifts at extraction. A dropped "no" inverted a rule. Exceptions vanished. "The fix is X" became "X is done". An agent's choice became "the owner decided".
- Stale facts win. Outdated facts were quoted even after a superseding memory existed.
- Recall fires on the wrong words. Searching with the user's prompt found the right memory in 0 of 14 cases; searching with the action's own words found it in 13 of 14.
- Transcripts pollute memory. Stored as memories, transcripts read as present-tense facts; about 42,600 transcript documents had to be deleted in one day.
- No scope boundaries. Plugins invented namespaces, there was no global layer, and records were moved between scopes by hand.
The cost shows up as anger. In the founding user's logs, profanity peaked on the days of a memory migration and of recall failures. A calm reminder usually came 1 to 2 minutes before each outburst: the cheapest moment to intervene.
Goals
The product covers twelve capabilities:
- One tool for agents to remember the owner's taste and decisions.
- Mining of all previous memories, transcripts, and preferences.
- A fix for each failure of existing memory tools (section 14).
- One cloud for all transcripts, stored raw, searchable as one corpus across devices.
- Dreaming: automatic memory linking, embedding, consolidation, and search.
- Tenant, repository, and user separation: repository knowledge stays in the repository scope, personal knowledge in the user scope, organization knowledge in the organization scope.
- One source of usage reporting, compatible with ccusage.
- Sign-in and mail on Better Auth and Resend.
- High-signal treatment of user corrections, anger, and decisions.
- Correct transcript handling across upload, resume, rewind, and fork, modeled on how agent clients store sessions locally.
- Claude.ai and ChatGPT on the web, through connectors and plugins.
- Banned-word routers that decide where a memory or transcript line may live.
Memory model
Memory is kept in three layers that never mix, and every precedent is anchored to a verbatim quote that code verifies.
Three layers
- Evidence: immutable transcript records (section 7).
- Precedents: one fact each, linked to the evidence span it came from.
- Views: profile and taste digests per scope, rebuilt by dreaming. They always cite precedent IDs and never act as rules.
Precedent fields
failurerecords an agent asking something already answered or repeating a mistake.negative_observationrecords "searched for S on date D, found no X", so an absence never turns into "X does not exist".- An agent quoting the owner becomes a
decision_recordcontaining the quote, never "the owner decided". - "The owner" is never resolved to a name.
- Every evidence turn is tagged by speaker: owner, agent, script, or timeout. A question card answered by a timeout script, or an unattributed line, binds nothing and never yields an owner precedent.
Write-time guards
Applied to every record call and every extraction:
- ID-shape validation.
- An episodic-note classifier that sends episodes and run tallies to the episode or transcript store, never to precedents.
- A secret and credential-location scanner at write time, plus a periodic sweep over stored precedents.
- A condition-drop check: a statement that loses "only when", "unless", "never", or "except" from its quote is refused.
- An extraction prompt owned per scope and versioned, with a bounded supporting-context length.
- A "canonical changed, review" signal whenever a per-scope override of a shared record exists and the shared record changes.
- Banned-word routers (section 10).
Amending, linking, supersession
- Amending. A correction writes a new version under the same ID in one atomic step. The old version moves into history and the new one is readable immediately.
- Supersession by key, not similarity. MemStrata found cosine similarity separates a contradiction from a duplicate at AUROC 0.59, near chance. Plain RAG served replaced values 15 to 40 percent of the time; the keyed approach, 0 percent.
- Ranking sees only current versions. A hit on a superseded memory follows the replacement chain to the newest version and returns that, with the old one attached as history: "used to be X, changed to Y on date D".
- Replacements inherit citation score, so a heavily cited old version cannot outrank its replacement. The old version keeps its own citation record for history but leaves the ranking.
Typed relations:
Decay and citation boost
Every kind decays at its own speed, with half-lives tuned against the recall evaluation (section 6):
- Version expiry. A version-specific gotcha ("library 1.7.7 swallows OTP errors") expires when the version it names is gone.
- No automatic forgetting of rules. Rulings, conventions, and preferences end only when replaced or retracted. One that sinks below a threshold goes to a "still true?" queue in the digest email.
- Exact-trigger exception. An action that matches a ruling's exact trigger returns the ruling, however old.
- Citation boost, modeled on academic citations. Being quoted (an agent cites the ID, acts on it, or the owner restates it) counts far more than being returned. The owner's restatements and corrections count most, as does a broken rule that angered the owner. The boost is log-scaled, recency-weighted, and propagates over the link graph like papers citing papers.
Exact-trigger matches override the score. Retracted and superseded records appear only as history.
5. Recall
Recall reaches the agent just before a state-changing action, searched with the action's own words, and stays silent unless a rule binds.
Two tools
recall(read-only,readOnlyHint: true): query, optional scope and limit. The one tool agents are told to use. Read-only tools are auto-approved by Claude, and ChatGPT Pro permits only read and fetch tools.record(write, not destructive): saves a precedent or decision.- Read and write stay separate tools. A single tool with an action argument would prompt for approval on every recall and fails connector review.
- Tool names stay under 64 characters with factual descriptions. Behavioral guidance lives in the plugin skill and the server
instructions, never in tool descriptions.
Fast path (hooks)
- No model call on a silent hook call. The hook path is one search query: BM25 plus vector fused by rank, scope filters, latest versions only, plus an exact trigger-index lookup. Embeddings are computed inside the search index, so a recall is one request.
- Parse the action first (command, subcommand, flags, paths, repository, recipient) and recall with the action's words.
- Adaptive score gate. Inject only when a rule binds, just before a state-changing action. At most about 2,000 characters are inserted, below every client's context limit, under the heading
Eidetium recall (<depth> depth):. A call that names neither an event nor an action is treated as an explicit search. - One-hop expansion over
supersedes,refines, andviolated_by. - Query rewriting by a model happens once at session start or once per prompt, cached.
- Per-session dedupe. A precedent or link already injected in a session is not injected again; links are compared after normalization (host lowercased without
www.,https, no fragment or trailing slash, no tracking parameters, sorted query). Both reset on compaction. - Standing context. Dreaming precomputes per-project context for session start, and session start sends a cache-warm request to the search index.
- Depth is always stated. An empty result reads "not found at depth N", never "does not exist". Coined terms match exactly and are never respelled.
- Timing. Pre-action where the harness allows it (Claude Code, Codex); post-action in Cursor and Pi. Recall runs again after compaction.
Relevance gate
Before anything is inserted, a small fast model judges each candidate against the trigger and what the session already received. It keeps only what is new or conflicts with what the session has, drops the irrelevant, the repeated, and what the agent already knows, and states why.
- It runs only when there is something to insert. A trigger-index match is inserted without it.
- When the gate errors or misses its deadline, binding precedents are still inserted and web results are dropped. A binding rule is never lost to a timeout.
Web results
Recall adds up to 3 web results beside memory results, inside the same 2,000-character budget, each as link, title, and matching snippet.
- Searches run through Firecrawl with the trigger's words, masked by the redaction package before the query leaves the server.
- They run at session start, on the prompt, on a state-changing PostToolUse, in an explicit search, and in the deep path. Never on a read-only call, never in PreToolUse, never inside the deep loop's own searches.
- A hook call inserts web results only when the relevance gate can judge them.
- Session start and prompt calls never wait for the web: they answer with memory plus any stashed web hits, and the search finishes in the background and is stashed. State-changing PostToolUse, explicit search, and the deep path wait for a fresh result up to the deadline.
- The stash lives in Postgres under the tenant's row-level security, holding a session's 20 newest pages for a day, because a hook's next call can land on another function instance. Later state-changing hook calls insert stashed hits the gate passes.
- Web results are on by default. An org admin can turn them off or on for the tenant; each change is audited.
Latency targets
Short-lived agents pull on startup and upload at the end, so session-start recall and the final write are the two hot paths.
Deep path
For prompts, compaction, and on demand, recall runs an agentic search over precedents and evidence. On LongMemEval-V2, agentic search scores 81 percent against 43 percent for plain RAG, at about 100 times the latency.
- It returns evidence units: the current ruling, its superseded history, and any conflicts.
- Transcripts are searched only on request and always carry timestamps.
- The loop runs on the tenant's own model route (section 8). With no route, or when a model call fails, the fast answer is returned, marked
fast. - Every precedent a tool returns passes the recall egress filter, and every transcript line passes the tenant's current banned terms, so a line banned after indexing never reaches the model.
Delivery per client
Dreaming
Dreaming turns raw evidence into grounded, linked, verified precedents in eleven stages, and no evidence segment ever finishes without a recorded outcome.
Pipeline
- Ingest. Append evidence with one stable ID per source record and a content hash.
- Segment. By turn plus a window of 3 to 4 prior turns, keeping the agent action the owner reacts to in the same segment.
- Signal-score. A code first pass, then a fast model scores what passes, batched where the route allows. Stop and retraction orders skip the queue and publish immediately; high-signal segments process instantly; the rest batch.
- Extract with grounding. A strong model returns statement, quote spans,
asserted_by, kind, scope, triggers, andsubject_key. Code then checks: 5. each quote is an exact substring of the cited evidence; 6.asserted_by = owneronly when the span's speaker is the owner; 7. every clause is entailed by the quote, else the precedent falls back to quote-only; 8. an absence becomes anegative_observationwith its query and date. - Record an outcome for every segment: precedents produced,
no_signalwith a reason, orfailedand queued for retry. - Dedupe and merge. kNN proposes candidates; a merge happens only if the newer record keeps every clause; ambiguous cases do nothing.
- Supersede by
subject_keywith overlapping scope. The latest owner word by event time wins, a narrower scope refines rather than overrides, owner beats agent, and the change cascades toderived_fromchildren. - Link. Typed relations via kNN plus a model. Only links and tags change; neighbours' content is never rewritten.
- Summarize. Rebuild profile and taste views per scope, citing IDs.
- Verify with a model from a different vendor: re-read a sample of new precedents against their quotes, plus every derived claim, and reject "owner decided" claims that fail the speaker check.
- Publish. An atomic version bump makes every agent see the change on its next query. Stop orders and rulings push a notification.
Scheduling and execution
- Stages 1 to 5 run on ingest; high-signal segments finish in seconds. Stages 6 to 9 run when a thread goes idle and nightly. Stage 10 runs nightly with the recall evaluation.
- Dreaming uses stronger, slower models than recall: a fast model scores, a strong model extracts the 10 to 20 percent of segments that score high, and a model from another vendor verifies.
- Work runs as durable workflows split into small segment-sized steps, with streamed model calls as their own step pattern.
- Step IDs stay stable across deploys and step outputs only widen, so steps stay idempotent.
- A nightly parent run fans out child runs per user and per batch of segments; no single run covers a whole user or a backfill.
- Per-tenant fairness: one organization's backlog cannot starve the others.
High-signal moments: corrections, anger, decisions
A code first pass flags candidate segments; a model then scores them. The cues are English:
- Anger words, insults, spaced-out capitals, and "who made X" attribution asks.
- Repeated-correction phrasing ("did I not", "how many times", "again"), the strongest signal.
- Shouting: all caps, runs of ? or !.
- The same message re-sent within 120 seconds.
- Quoting the agent's line back with ">".
- Ruling openers: "remember", "from now on", "never", "always", "amendment", "new owner ruling".
- Retirement and staleness corrections ("X IS RETIRED"), the case where a stale precedent beat a newer one.
- Negation and "no, instead" patterns.
- The owner's turn right after a tool error, and the
[Request interrupted by user]marker. - Free-text answers on question cards, from the owner only.
- Interrupts and stop orders.
- Permission and tool-call denials, the owner editing or reverting an agent's diff, and the owner closing a pull request. Diffs, issues, and pull requests count as evidence.
- A strong match to an existing precedent. A strong match plus anger means recall failed: it is logged automatically as a
failureand a recall-evaluation case.
Plain anger-word matching misreads technical text ("kill the process"), so cues only route segments to model scoring and never decide alone. On a hit:
- Stop and retraction orders take effect immediately.
- A correction rewrites or replaces the precedent, links it to the one it overrode, and adds a recall-evaluation case.
- Anger raises priority and sends the segment to careful extraction, but never becomes the rule's content.
- Every owner answer to a question card or correction is stored with the owner's exact words.
Recall evaluation
Every logged failure becomes a test case: the exact action or prompt text, the precedent IDs that must appear, and the IDs that must not.
- Nightly report: must-include recall@k on the fast path, stale-fact rate, false-attribution rate ("owner decided" with no owner span), clause-loss rate, and empty-outcome counts.
- Judge testing: every release tests the judge with deliberately wrong but on-topic answers.
- External checks: LongMemEval-V2 gotchas and premise sets, and STALE.
- Baseline: the founding user's existing 59-question recall evaluation is the first gate.
Transcript cloud
A device program uploads every agent transcript as content-addressed records, so resume, rewind, fork, and multiple devices merge into one lossless, searchable corpus.
How agent clients store sessions
Claude Code writes ~/.claude/projects/<cwd slug>/<session id>.jsonl, one JSON record per line in a uuid/parentUuid graph, with subagents in …/subagents/agent-<id>.jsonl (verified on a 400-session sample).
- Parallel tool calls make the graph look branched (635 of 638 observed branch points). Siblings under one assistant
tool_useare treated as one turn. api-request*records hold full system prompts and are dropped or redacted by default.- The parser accepts the legacy
summaryrecord of older versions and hashed, truncated project folder names. - Claude Code deletes transcripts after
cleanupPeriodDays(default 30), so backfill runs before that sweep.
Upload and sync
- The device program is the uploader; hooks only nudge it. Transcripts are written asynchronously and may lag when a hook fires, and SessionEnd has a short budget. Hooks write
{source, session_id, transcript_path}to a local spool in under 50 ms, with no network call. - Watching. The program watches every source root (inotify or FSEvents, 2 to 5 s debounce, 60 s rescan), keeps a durable SQLite outbox with backoff, and runs a one-time oldest-first backfill.
- Records, not byte ranges. Each record is zstd-compressed canonical JSON keyed by
blake3(canonical record). An occurrence row points to that hash with device, source, session, file, native ID, parent ID,logicalParentUuid,forkedFrom, byte offset, and timestamp. An occurrence is unique per device, source, session, and native ID. - Shared blobs. Large repeated payloads (instructions, prompt snapshots, skill listings, tool-result files) go to a blob store keyed by hash, uploaded once across sessions and devices. Attachments are 47.5 percent of bytes, and about 70 percent of those repeat.
- Per-file cursor: device, inode, size, mtime, offset of the last complete newline, and hashes of the first 4 KB and last synced line. A matching inode and last-line hash means append; anything else re-parses the file and diffs by native ID. Nothing already on the server is deleted; missing records are marked "absent locally".
- Resume is more appends. Rewind is an extra child edge, and every branch is kept. Forks become new occurrences of the same content hashes (near-zero storage) plus an alias edge to the parent record when one is named.
- Current view follows the last
last-prompt.leafUuid, else the newest conversational leaf, walkingparentUuidto a root and hopping compact boundaries. All branches are indexed for search and tagged when off the current path. - Multi-device merge is a set union keyed by source and native ID. Mutable side state (title, mode, last prompt) is last-writer-wins by timestamp and device. Order follows the graph, then ordinal or offset, then timestamp; never wall clock across devices. Server receive time is stored beside the client timestamp.
- Direct-to-storage uploads. The device uploads each blob straight to object storage through a presigned URL that signs content type and exact length, then commits through the API. Commit checks each staged upload's size and deletes any over the 64 MiB limit unread.
- Compression. zstd level 3 on the hot path, recompressed at level 19 with a per-source trained dictionary in the background.
- Design principle. Mirror the model, not the layout: append-only immutable records, an ID/parent graph, content addressing, edges separate from bodies, synthesized parent edges for sources without IDs, and local files treated as possibly rewritten.
Redaction on the device
Redaction runs before hashing, so secrets never leave the machine.
- A gitleaks-style ruleset plus entropy checks on JSON string values,
.envcontents, auth headers, credential locations, and the user's known-secret list (hashed locally; only HMACs leave the device). - Owner names and tenant terms are masked with X characters of the same byte length, so offsets stay valid for dedupe.
- Only per-line redaction counts are uploaded, never the matches.
- The raw local hash and a redaction-version tag are kept, so improved redaction can re-upload.
- Redaction before dreaming also keeps sensitive personal information out of model requests.
Storage and encryption
- Raw transcripts are stored in object storage as the durable copy, sealed with a key per tenant. The server can read them, which is what makes server-side search possible.
- The key-encryption key is wrapped by a master key held outside the database. Data is sealed with XChaCha20-Poly1305: each data key bound to the tenant ID, each blob bound to tenant ID, key version, and blob hash.
- Embeddings cover precedents plus user and assistant text, under 2 percent of Claude Code bytes. Tool output and attachments are keyword-searched on request.
- Customer model credentials (OpenRouter keys, ChatGPT tokens) are encrypted with the tenant key, never logged, and decrypted only in workers.
- Sizing reference: the founding corpus is about 72 GB raw, about 10.5 GB compressed, growing about 1.5 GB a day raw. Measured zstd ratios run 6.5× to 10.5×.
End-to-end encrypted transcripts (after v1)
An opt-in mode for personal tenants seals transcripts on the device under a key the server never holds. No encryption scheme keeps server-side search and model calls over the same ciphertext, so the mode is a hybrid.
- Keys. Enrolled with a passkey's PRF output plus a required recovery code. A new device receives the key sealed to it during device approval. There is no escrow: losing every device, passkey, and the recovery code loses the transcripts.
- Eligibility. Personal tenants only. Organization tenants get a customer-managed key with purge on revocation instead.
- Naming. The mode is called "end-to-end encrypted transcripts" and lists beside its name what the server still reads. The product as a whole is never called end-to-end encrypted.
- Groundwork in v1: a versioned key envelope with room for server, customer, and device key modes; redaction and the line router in packages the device program runs; any feature that reads raw transcript text counted as a cost to this mode; a privacy policy and subprocessor list that say plainly who reads what.
Export and deletion
- Export: JSONL of precedents plus a transcript tarball. The tarball is sealed with the tenant key in framed XChaCha20-Poly1305, and its signed, expiring link points at an authenticated download that decrypts while streaming, only to the requester. It is kept 24 hours and withdrawn when any deletion is requested.
- Deletion: soft delete, then hard delete after a set period, a vector purge, and crypto-shredding by destroying the tenant key.
- What survives deletion: only anonymized usage aggregates per day, agent, and model. They name no tenant, user, device, session, file, or record; models without an exact catalog name, or with fewer than 10 requests that day, fold into
other. The application can only append to this table, never read it. - Audit log: hash-chained and append-only (actor, action, scope, target, IP), visible to org admins.
Usage reporting, ccusage-compatible
The transcript corpus is the single source for usage. The server reimplements ccusage's accounting and serves the same JSON shapes.
- Deduplication per user across all devices and forks:
message.idplusrequestIdfor Claude,response_idfor Codex. - Event-time pricing, recording which pricing snapshot was used. Usage is always re-derived from raw records, never from stored totals.
- Report shapes:
daily,weekly,monthly,session, andblocks, with a unifiedperiodplusagents[], andtimezoneandstartOfWeekparameters. - Improvements over per-host scripts: cache tokens reported separately from input,
cacheTokensandrequestCountfields, pinned pricing data, cross-device dedupe, and days bucketed in one timezone. - Parity gate: a fixture diff against a pinned
ccusage --offline(tokens exact, reported cost to 1e-9), plus a nightly parity run on real uploads. Fixtures cover duplicate IDs across files, missingrequestId, gateway placeholder IDs, sidechain and/btwreplays, advisor iterations,speed: fast,<synthetic>, cache-write and long-context edge cases, and timezone boundaries. - Usage ledger: every model call Eidetium itself makes is tagged with feature, session, user, and route, and recorded through OpenTelemetry spans.
Model calls
One worker codebase on the AI SDK picks a model route per customer at request time, preferring the customer's own route and never moving them to a managed route without opt-in.
Routes, first available wins
- The customer's ChatGPT plan, through Sign in with ChatGPT. A later feature; it takes first place when it ships.
- An OpenRouter key the customer connects.
- Eidetium-managed OpenRouter.
- Eidetium-managed Vercel AI Gateway.
Cross-vendor verification (dreaming stage 10) always runs on a route that reaches a different vendor from extraction.
Route selection
- A
resolveModel(job, tenant)function selects the route, with job names (score, extract, verify, profile, classify, gate, deep) aliased to models. - Every call is tagged with tenant, organization, job, and scope.
- Workers check the error type before retrying, because a gateway can report a rejection as an internal error.
Privacy settings on every call
- Zero data retention, no training, and only providers that support every request parameter.
- No server-side tools or plugins on the provider side, since zero retention does not cover them.
- The
userfield carries a hashed tenant ID. - Fallbacks stay within the same model family.
- Input and output logging stays off on every key Eidetium issues; models that retain or train on inputs are never used.
- Onboarding checks the customer's own prompt-logging setting, which Eidetium cannot change.
Connecting a customer's OpenRouter account
Customers connect by approving Eidetium on OpenRouter's own page, with no key pasting. They receive a scoped, expiring key they can revoke, and the flow works on headless machines.
Sign in with ChatGPT (later)
OpenAI's Sign in with ChatGPT lets Eidetium's server call the Responses API with the user's OAuth token, running on the user's ChatGPT plan.
- Every call sets
store: falseandstream: true, sends the system prompt ininstructions, and omits the parameters the program rejects. - Function tools work; hosted tools do not.
- Tokens are stored encrypted with the tenant key and refreshed in the worker; refresh tokens rotate.
- A call succeeds only on
response.completed, since a usage-limit error can arrive after streaming begins. - The account's models come from
GET /v1/modelswith the user's token.
Claude subscriptions are not a route: Anthropic does not permit third-party apps to sign in with Claude.ai or route requests through its consumer plans.
Embeddings
- The search index's built-in embedding with Voyage
voyage-4-lite(1024 dimensions, 32,000-token context), so a recall is one request. - Changing the embedding model means re-ingesting into a new namespace, dual-written during the switch.
- Multimodal content would use Voyage's multimodal model called directly, with vectors written to the index.
Scopes, tenancy, identity, mail
The server assigns every memory to a repository, user, or organization scope inside exactly one tenant, and every credential is pinned to that tenant.
Scopes
- The server decides from raw client signals: working directory, git remotes, the repository's first commit, GitHub repository ID, device, agent, session. Within a tenant, an explicit
scope: repo | user | orgfrom the client wins. Across tenants, never. - Repository identity combines the normalized remote, the GitHub repository ID (survives renames), and the root commit (forks and remote-less repositories). Worktrees resolve to their main checkout.
- Classification order: explicit marker, then deterministic tenant match, then a model choosing the scope inside the tenant (in dreaming, never in the hook), then quarantine plus a "decision awaiting you" mail. A banned-word hit always routes or asks (section 10).
- Moves. Dreaming can move a memory to another scope later; each move is logged and linked like a supersession. Every owner override is stored as a labeled example.
- Precedence: the owner's words beat an agent's; the latest word wins for the same subject key; a ruling beats a convention; ties go repository, then organization, then user.
- Mixed sessions. A session that touches two tenants or repositories is split line by line.
- Shared rules flow one way as server-side subscriptions, never physical copies (states: variant, conflict, held, banned), so drift cannot happen. Nothing is ever copied out of a work tenant.
Tenant isolation
- A tenant is a Better Auth organization. Every user gets a personal organization, recreated at sign-in if missing and never deletable while the account lives.
- Isolation is enforced five ways: Postgres row-level security on every table, a per-tenant search namespace, a per-tenant blob prefix, a per-tenant key-encryption key, and grants pinned to one tenant.
- The application database role cannot bypass row-level security, and
FORCE ROW LEVEL SECURITYis set on every table. Tenant and user are set per transaction. - Records held by a router are readable only by their author.
- Authentication tables live in a separate schema reached only by a separate role; neither role can read the other's tables.
- A test against a throwaway Postgres asserts the application role cannot read another organization's rows.
- Search namespaces carry environment, tenant, and embedding version in their name, with repository and scope as filter attributes.
Identity (Better Auth)
- Sign-in: email one-time codes (6 digits, 300 s, 5 attempts, hashed storage) and passkeys, with disposable-domain blocking and rate limits.
- Organizations with teams and dynamic access control; multiple sessions; admin tools. Invitations survive sign-in.
- Device authorization for the device program: each approval is its own refresh-token grant, bound to one organization the approver belongs to, revocable on its own.
- MCP OAuth for Claude.ai and ChatGPT: client ID metadata documents with dynamic client registration as fallback, S256 PKCE, protected-resource metadata (RFC 9728), resource indicators (RFC 8707), and issuer identification (RFC 9207).
- Opaque, revocable access tokens, stored as hashes. Refresh tokens rotate, and replaying a rotated token revokes the client's tokens.
- Two resources: the MCP endpoint for connectors and an API resource for the device program. A token reaches only the surface its resource names; a browser session reaches the API but never MCP.
- Connector grants are pinned to one organization, chosen on a consent page that names it and checked against membership.
- Organization-scoped API keys only for automation and headless servers that cannot run a device flow; they die with their organization.
- MCP protocol: both the 2025-11-25 and 2026-07-28 versions, served statelessly from one handler, so older clients such as Claude.ai and ChatGPT are never locked out.
- Server
instructionsput the key points in their first 512 characters.
Mail (Resend)
One send function adds a timeout, an idempotency key, test-mail marking, a recipient and content blocklist fed by the banned-word routers, and bounded retries on 429 and 5xx.
Mails Eidetium sends:
- Sign-in codes, device approvals, "new device signed in".
- Organization invitations.
- Security notices: new connector authorized, API key created, export ready, deletion scheduled.
- The nightly dream digest.
- "Decisions awaiting you": unclear scopes, conflicts, banned-word hits.
- The "still true?" queue.
- A weekly taste and usage report.
10. Banned-word routers
Per-tenant term lists decide where a memory or transcript line may live, enforced on the server everywhere data moves.
Term lists and actions
Lists exist per tenant and per account and hold company names, client names, codenames, people, hosts, and repository names. Each term carries one action:
Where routers apply
recordwrites and dreaming extraction.- Transcript ingest, split line by line when a session touches two tenants.
- Recall egress: a result naming another tenant's terms is withheld from the requesting scope.
- Shared-rule fan-out, which stays one-way and never copies out of a work tenant.
- An optional outbound check clients call before posting text such as pull request bodies, issues, and comments.
Rules
- A rewrite that drops a tenant term is refused the same way as one that adds it, so a record cannot launder itself out of its tenant.
- Matching is case-insensitive at word boundaries, with a pinned dictionary so ordinary words ("backend") do not trip it.
- Terms are stored as split halves or hashes, so no source file or log spells a banned term.
- Every route, ask, and ban is audited with the term ID (never the term), the scope, and the decision.
- A tenant's record goes only to that tenant, and a tenant's namespace takes only its own records and shared rules.
- A
Namespace hintline pins a record to a namespace. - Recall reads the account scope, plus a tenant scope only when the working directory is that tenant's pinned checkout.
- Records never hold secrets or a map of where secrets live. Owner names live only in host configuration, never in a memory.
11. Clients and distribution
Eidetium ships as one remote MCP server, one plugin repository for coding agents, and one device program, distributed directly rather than through app directories.
- Remote MCP server. Streamable HTTP on both protocol versions, Better Auth OAuth, tools
recallandrecord, and serverinstructions. - One plugin repository with
.claude-plugin/marketplace.json, read by Claude Code and by the ChatGPT desktop app. It holds an HTTP.mcp.json, theeidetium-precedentsskill, andhooks/hooks.json:- a SessionStart command hook (launch profile);
- an
mcp_toolrecallhook on SessionStart after compaction; mcp_toolrecallhooks on UserPromptSubmit, PreToolUse, and PostToolUse (Bash, Edit, Write, selected MCP tools), passing the command;- an async Stop nudge to the device program.
- Codex reads
.codex-plugin/plugin.json, which names its ownhooks/codex.json, plus an agent-pluginsmcp.json. The same hook scripts run in both. The plugin carries no rootplugin.json, because Codex would prefer it and load no hooks. - Device program on each machine: backfill and live sync of Claude Code, Codex, and Cursor transcripts, plus T3 metadata (read-only).
- Claude.ai: add the server as a custom connector and upload the same plugin so its skill loads; Claude chat ignores plugin hooks. Export ZIPs import for backfill.
- ChatGPT: custom MCP or plugin on the web; full read and write on Business and Enterprise/Edu, read and fetch on Pro. Export ZIPs import for backfill.
- Cursor: picks up Claude Code user hooks; the MCP server is added to
~/.cursor/mcp.json. - Gemini CLI: an extension with
gemini-extension.json, hooks, and skills. - Browser extension: last and optional; passive, opt-in reading of the page, since both chat vendors prohibit scraping.
- No directory listings. Claude and ChatGPT directory review rejects tools that pull behavioral instructions from external sources, which is Eidetium's purpose. Eidetium ships as a custom connector and an uploaded plugin.
12. Platform and architecture
Eidetium runs as one Vercel Services project in a Turborepo monorepo, colocated with its database, search index, and object storage in Oregon.
Region
- Default region: Oregon. Vercel
pdx1, search index inaws-us-west-2, Postgres inus-west-2, object storage in US West. - A per-tenant
home_regionexists in the data model from the start. Adding compute regions is configuration; database, search, and storage are single-region per deployment, and workflows pin each run to its starting region.
Hosting
- Several apps share one domain through Vercel Services, deployed and rolled back together.
- Workers run on the Workflow SDK and Queues. A workflow run stays on the deployment that started it, so schema changes add columns before removing old ones.
- Previews run in the production region with their own Postgres branch, search namespace prefix, and storage bucket.
- The hook path never takes a service-to-service hop.
- Postgres is reached through a small connection pool per function.
- Object storage is Backblaze B2 through its S3-compatible API, with one bucket and one scoped key per environment, a lifecycle rule that removes hidden object versions, and default server-side encryption beneath tenant-key sealing.
- Cron runs on production only, behind a bearer secret.
Stack
The interface follows Apple-style restraint: Tailwind's default scale, a shared component layer, small subtle icons, no eyebrow labels or redundant captions.
Monorepo layout
Routing sends /api and /v1 to the API, /.well-known/oauth-* and /mcp to the MCP server, /docs to docs, and everything else to the web app. Package boundaries are enforced: the device program imports neither the database nor auth, the UI imports no server package, and only the database package imports the ORM.
Engineering principles
- Secrets. Three encrypted environment tiers (development, preview, production) are bundled into each function and decrypted at runtime, so the host holds only the private key. One namespace variable prefixes every resource, so environments never collide.
- Deploys. Vercel builds a preview for each pull request and production for each push to the main branch. A migration job runs on each main-branch push, and production deployments wait for it to pass. Pull request previews never migrate the shared preview database.
- Migration safety. A check that two branches did not add migrations on the same parent, a check that blocks dropping a column the current code reads, and add-then-remove schema changes.
- ClickHouse migrations are numbered, checksummed, single-statement, and idempotent, with least-privilege users (an insert-only writer, one user per drain).
- Build guard. Every built server module is imported once at build time, so a module that crashes on load fails the build.
- Dependencies. A 3-hour release cooldown, a check that every patch still applies, grouped weekly updates.
- Checks. All checks run in one task graph in the pre-push hook, under 2 minutes, and a merge gate reruns them on the head merged onto the newest main branch. Database, ClickHouse, storage, and end-to-end suites each run on their own containerized stack.
- Tests are end-to-end and assert behavior, never existence or copy. The row-level-security isolation test and the ccusage parity fixtures are the first suites.
- Licensing. No AGPL or GPL code (ParadeDB, VectorChord's BM25, Typesense, Basic Memory). Apache and MIT projects (Graphiti, mem0, Hindsight, BGE-M3) may be studied and adapted.
Subprocessors holding customer data
Each vendor account belongs to a dedicated Eidetium team or organization, separate from any other projects.
13. Importing existing history
Eidetium starts from everything a user already has: prior memories, every local transcript, chat exports, and repository history.
- Supermemory. Imports a user's namespace on their own key, keeping IDs, as precedents with their history. The founding namespace holds 6,240 documents (2,248 gotchas, 1,319 findings, 826 conventions, 694 rulings, 583 decisions, plus smaller and legacy types). Legacy types are mapped onto the precedent kinds. Projects are keyed on a stable project identity rather than free-text ID prefixes, since one project can appear under several spellings and some IDs are service-random.
- Transcripts. The device program backfills every local store oldest first: Claude Code, Codex, Cursor, Grok, Pi, Muse Code, with T3 as metadata. The founding corpus is about 72 GB raw.
- Repository history. GitHub issues and pull requests, plus records from older machines.
- Claude.ai and ChatGPT. Export ZIPs import with their branch trees and memories.
- Mining. Dreaming over the backfill extracts precedents, failures, and high-signal moments. Labeled owner turns calibrate the signal detector.
14. Comparison with existing memory tools
Every failure observed in the founding user's Supermemory history has a structural fix in Eidetium, not a workaround.
What Eidetium makes unnecessary
The manual harness users build around today's tools: stage-then-swap rewrites, periodic rule-sync reconciles, copying shared rules into every namespace, and hooks that deny MCP writes. Eidetium builds in instead: server-side version history, tenant ownership and scope egress policy, verification of memories against their source, banned-word routers, and the recall evaluation.
Two further lessons are built in. Memory policy gets rewritten often (eight times in two weeks for the founding user), so scope configuration and extraction prompts are versioned. Issue trackers increasingly link to rules instead of copying them, so every precedent has a stable decision link that tickets and pull requests can point at.
15. Roadmap
Eidetium ships in five phases, each usable on its own, with the recall evaluation as the gate for the first release.
- Phase 0: Spike. Load the founding user's precedents into a search namespace in Oregon. Measure hook latency warm and cold, embedding time, and cold start on a small index. Port the 59-question recall evaluation as the gate.
- Phase 1: Precedent core, for a single user. Schema, versioning, subject keys, the scope classifier,
recallandrecord, the Claude Code and Codex plugin, import from Supermemory keeping IDs, banned-word routers. Ships once the recall evaluation beats Supermemory. - Phase 2: Transcript cloud. The device program, redaction, the content-addressed store, object-storage backup, backfill of the founding 72 GB, session graph rebuild, transcript search, and the usage API with the ccusage parity test.
- Phase 3: Dreaming. Signal scoring, grounded extraction, verification, linking, decay and citations, digests.
- Phase 4: Multi-user hosted service. Organizations and device grants, MCP OAuth for Claude.ai and ChatGPT, the chat plugin and export imports, tenant migration with one-way rules for work machines.
After v1: end-to-end encrypted transcripts, Sign in with ChatGPT as the first model route, the browser extension, and multimodal embeddings.