You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The GraphQL API returns no lifecycle metadata for a MetaEnvelope. MetaEnvelope exposes id, ontology, envelopes, parsed and _acl — a caller cannot tell when a record was created, when it last changed, which platform wrote it, or that it was deleted at all.
The information mostly exists already, but it is not reachable:
Neo4j MetaEnvelope nodes carry no timestamps at all (db.service.ts:92, :181, :617, :950).
EnvelopeOperationLog records operation, platform, timestamp and envelopeHash per write, but it is only readable through GET /logs, paginated per-eName and unfilterable by metaEnvelopeId — answering "when was record X created" means paging an entire eVault's history.
The log is written best-effort and fire-and-forget (graphql-server.ts:467), so a failed append silently loses the only trace of the write.
removeMetaEnvelope hard-deletes (DETACH DELETE, db.service.ts:549). After a delete the record is simply absent — a consumer cannot distinguish "deleted" from "never existed" or "no read access".
Platforms have each worked around this in the payload: 36 of 42 ontology schemas declare their own timestamp field (73 createdAt, 34 updatedAt, plus date, timestamp, sourceUpdatedAt, createdBy). These are inconsistent between ontologies and self-reported by the writing platform rather than recorded by the vault, so they can be wrong or absent, and adapters cannot rely on them for ordering or incremental sync.
Proposal: add a protocol-managed _meta block to MetaEnvelope, following the _acl convention (underscore prefix = vault-owned, not payload), and make it filterable.
Reference
infrastructure/evault-core/src/core/protocol/typedefs.ts — MetaEnvelope, _acl block as the naming precedent
infrastructure/evault-core/src/core/db/db.service.ts — node writes and deleteMetaEnvelope
infrastructure/evault-core/src/core/http/server.ts:253 — GET /logs
Acceptance Criteria
MetaEnvelope gains a _meta field returning at least: createdAt, updatedAt, createdBy (platform + eName of the writer), updatedBy, and a monotonic revision counter.
_meta is populated by the vault on write and cannot be set or overridden by the caller through MetaEnvelopeInput / BulkMetaEnvelopeInput.
bulkCreateMetaEnvelopes can preserve original timestamps during migration (mirroring how it already accepts a preserved id), gated to migration platforms the same way skipWebhooks is.
Deletion is observable: a caller can determine that a given id was deleted, when, and by which platform — either via a tombstone or via a log-backed history query (see design decision below).
metaEnvelopes(filter:) supports filtering on _meta (at minimum updatedAfter) and ordering by updatedAt, so adapters can do incremental sync instead of full scans.
The metadata write is part of the same transaction as the envelope write — a lifecycle record is never silently lost the way the current fire-and-forget log append can be.
Existing records without metadata return null fields rather than erroring; a backfill derives createdAt/updatedAt from EnvelopeOperationLog where entries exist.
Legacy queries (getMetaEnvelopeById, findMetaEnvelopesByOntology, searchMetaEnvelopes) keep working unchanged; _meta is additive.
Read access to _meta follows the record's existing ACL — no new inference channel about records a caller cannot read.
Ontology docs state that payload-level createdAt/updatedAt now mean domain time (when the event happened) and _meta means vault time (when the record was written), so the two are not confused.
Design decisions to settle
Denormalised vs derived. Storing createdAt/updatedAt on the node is cheap to read and easy to filter and sort on; deriving them from EnvelopeOperationLog keeps one source of truth but costs a scan per record. Suggested: denormalise the summary onto the node, keep the log as the authoritative history.
Deletion shape. Exposing deletion metadata requires choosing between keeping the hard delete and serving deletion facts from the log (e.g. a metaEnvelopeHistory(id) query), or switching to soft-delete tombstones. Tombstones make sync and "deleted vs never existed" trivial but change storage semantics and interact with the right-to-erasure story — worth deciding explicitly rather than by implementation.
Provenance granularity. Per-MetaEnvelope only, or per-Envelope too? Per-field updatedAt would let adapters merge partial updates without clobbering, but multiplies storage.
Desired Output (may vary)
Any consumer reading a record from an eVault can see, from the GraphQL body alone, when it was created and last modified, which platform made each of those writes, and whether it has since been deleted — without paging the operation log, and without depending on ontology-specific payload fields that the writing platform may or may not have set honestly.
Description
The GraphQL API returns no lifecycle metadata for a MetaEnvelope.
MetaEnvelopeexposesid,ontology,envelopes,parsedand_acl— a caller cannot tell when a record was created, when it last changed, which platform wrote it, or that it was deleted at all.The information mostly exists already, but it is not reachable:
MetaEnvelopenodes carry no timestamps at all (db.service.ts:92,:181,:617,:950).EnvelopeOperationLogrecordsoperation,platform,timestampandenvelopeHashper write, but it is only readable throughGET /logs, paginated per-eName and unfilterable bymetaEnvelopeId— answering "when was record X created" means paging an entire eVault's history.graphql-server.ts:467), so a failed append silently loses the only trace of the write.removeMetaEnvelopehard-deletes (DETACH DELETE,db.service.ts:549). After a delete the record is simply absent — a consumer cannot distinguish "deleted" from "never existed" or "no read access".Platforms have each worked around this in the payload: 36 of 42 ontology schemas declare their own timestamp field (73
createdAt, 34updatedAt, plusdate,timestamp,sourceUpdatedAt,createdBy). These are inconsistent between ontologies and self-reported by the writing platform rather than recorded by the vault, so they can be wrong or absent, and adapters cannot rely on them for ordering or incremental sync.Proposal: add a protocol-managed
_metablock toMetaEnvelope, following the_aclconvention (underscore prefix = vault-owned, not payload), and make it filterable.Reference
infrastructure/evault-core/src/core/protocol/typedefs.ts—MetaEnvelope,_aclblock as the naming precedentinfrastructure/evault-core/src/core/db/db.service.ts— node writes anddeleteMetaEnvelopeinfrastructure/evault-core/src/core/db/migrations/backfill-envelope-operation-logs.ts— existing backfill precedentinfrastructure/evault-core/src/core/http/server.ts:253—GET /logsAcceptance Criteria
MetaEnvelopegains a_metafield returning at least:createdAt,updatedAt,createdBy(platform + eName of the writer),updatedBy, and a monotonicrevisioncounter._metais populated by the vault on write and cannot be set or overridden by the caller throughMetaEnvelopeInput/BulkMetaEnvelopeInput.bulkCreateMetaEnvelopescan preserve original timestamps during migration (mirroring how it already accepts a preservedid), gated to migration platforms the same wayskipWebhooksis.idwas deleted, when, and by which platform — either via a tombstone or via a log-backed history query (see design decision below).metaEnvelopes(filter:)supports filtering on_meta(at minimumupdatedAfter) and ordering byupdatedAt, so adapters can do incremental sync instead of full scans.nullfields rather than erroring; a backfill derivescreatedAt/updatedAtfromEnvelopeOperationLogwhere entries exist.getMetaEnvelopeById,findMetaEnvelopesByOntology,searchMetaEnvelopes) keep working unchanged;_metais additive._metafollows the record's existing ACL — no new inference channel about records a caller cannot read.createdAt/updatedAtnow mean domain time (when the event happened) and_metameans vault time (when the record was written), so the two are not confused.Design decisions to settle
createdAt/updatedAton the node is cheap to read and easy to filter and sort on; deriving them fromEnvelopeOperationLogkeeps one source of truth but costs a scan per record. Suggested: denormalise the summary onto the node, keep the log as the authoritative history.metaEnvelopeHistory(id)query), or switching to soft-delete tombstones. Tombstones make sync and "deleted vs never existed" trivial but change storage semantics and interact with the right-to-erasure story — worth deciding explicitly rather than by implementation.updatedAtwould let adapters merge partial updates without clobbering, but multiplies storage.Desired Output (may vary)
Any consumer reading a record from an eVault can see, from the GraphQL body alone, when it was created and last modified, which platform made each of those writes, and whether it has since been deleted — without paging the operation log, and without depending on ontology-specific payload fields that the writing platform may or may not have set honestly.