Le contrat publie et l'api-contract etaient traites avec le retrait lui-meme ; restaient les documents qui decrivaient readUnion comme la primitive de lecture du paquet, ou qui nommaient le module disparu. Ce que les lecteurs apprennent maintenant : lire un document, c'est docs.sparqlQuery ancre dessus, et lire plusieurs documents, c'est cet appel une fois par document -- avec la decision sur une lecture qui echoue ecrite au point d'appel. Deux consequences sont dites plutot que decouvertes : une lecture refusee leve au lieu de rendre une liste vide, et les sujets sous le prefixe reserve urn:ng-eventually: reviennent dans le resultat, a l'appelant de les ignorer -- sans aucun gain de droits, les deux registres qu'un document ordinaire porte etant l'adresse de sa boite et, pour un document public, la cle que son magasin sert deja a qui la demande. Les briefs dates ne sont pas touches : ils decrivent un etat de juillet, et le reecrire falsifierait ce qui avait ete decide alors.
9.9 KiB
type, summary, last_checked
| type | summary | last_checked |
|---|---|---|
| knowledge | What NextGraph's own IRI vocabulary is, what this package writes instead, and why scaffolding triples and durable application data must not share a namespace | 2026-08-21 |
What an IRI says upstream, and what ours says
Read at the source in nextgraph-rs (213338f6). Every claim points at a symbol or a module rather than a line, so git grep settles any doubt after the code moves. Two questions are answered here and they are not the same one: what does the target's vocabulary look like? and what does a triple this package writes promise about its own future?
Upstream defines exactly one namespace, and it is the engine's
NG_ONTOLOGY = "did:ng:x:ng#" (engine/verifier/src/types.rs) is the only vocabulary namespace anything upstream declares as a constant. Three terms sit beside it — NG_ONTOLOGY_ABOUT (#a), NG_ONTOLOGY_TITLE (#n), NG_ONTOLOGY_CLASS (#c) — a document's description, its title, its class.
Those three are not the whole namespace. The same did:ng:x:ng# carries engine protocol state, written as inline string literals rather than named constants: ng:site / ng:protected and their _inbox counterparts, which import_contact_from_qrcode writes into a contact document (engine/verifier/src/request_processor.rs), and ng:social_query_id / ng:social_query_forwarder / ng:social_query_started / ng:social_query_sparql, which the social-query machinery reads and writes (engine/verifier/src/inbox_processor.rs). So the namespace is engine-owned in a broader sense than "document metadata": it is where the engine keeps what it needs to keep.
Everything else that looks like a NextGraph vocabulary lives only in test fixtures: did:ng:x:contact#…, did:ng:x:core#… and did:ng:x:contact:class#… in sdk/rust/src/tests/big_contact_schema.json, contact_data.sparql, orm_create_patches.rs, orm_creation.rs. No engine code writes them. There is no registry, no parser, no stability promise behind any of them.
x is not an address — vocabulary and addresses share a scheme and nothing keeps them apart
The NURI grammar knows o v b c d a g h j k l n s t u and the bare did:ng:i — the regexes in engine/net/src/types.rs and every constructor of NuriV0 (engine/net/src/app_protocol.rs). Neither x nor z nor ex is among them: a vocabulary IRI is simply a string that starts like an address and matches no rule.
The fixtures make the point sharper. They also use did:ng:k:contact:tag#… and did:ng:k:contact:phoneNumber#… for controlled values — and k is in the grammar, as the key half of a :j:…:k:… or :c:…:k:… pair. The letter is already spent; the vocabulary form matches nothing anyway. Nobody arbitrated that collision because nobody had to: no registry exists to arbitrate it in.
did:ng:x:core#value is not "a field's value"
It is the payload slot of a reified value node, and reading it as a generic field accessor gets the model backwards. The shape, verifiable in sdk/rust/src/tests/contact_data.sparql:
- the entity carries
did:ng:x:contact#email→ a value node (its own IRI); - the value node carries
did:ng:x:core#value→ the literal; - the value node also carries the qualifiers:
core#source(where this value came from),core#type(an IRI such asdid:ng:k:contact:phoneNumber#home),core#hidden,core#selected.
The field name lives in the parent's predicate, never in the value node. And the payload slot splits by RDF term kind rather than by field: core#value holds a literal, core#valueDateTime / core#valueDate a typed date, core#valueIRI an IRI. :class# (as in did:ng:x:contact:class#SocialContact) is a convention observed in exactly one vocabulary, the contact fixture.
There is no extension convention, and nothing upstream filters or reserves
No reserved segment, no procedure to claim one, no prohibition — nowhere in the tree. What the examples do instead is either PREFIX ex: <did:ng:z:> (sdk/js/orm/src/tests/shapes/shex/testShape.shex, the shex under sdk/js/examples/, sdk/rust/src/tests/video_schema.json) or plain web ontologies — vcard, foaf, schema.org, which the engine itself uses for a contact's name and email. One example spells did:ng:n:g:x:social:contact# (sdk/js/examples/react-ldo/src/.shapes/contact.shex and the generated files beside it), an older form matching neither the fixtures nor the grammar: the scheme is not self-consistent across the tree.
Nothing upstream filters or reserves a namespace. The engine stores and returns arbitrary IRIs. The single prefix-sensitive path is in engine/verifier/src/commits/transaction.rs, which matches NG_ONTOLOGY_ABOUT / NG_ONTOLOGY_TITLE among a commit's inserts and removes only to push a tab-info update when a title or description changed — and only for updates whose branch_type.is_header(). The triples stay in the store and stay queryable; nothing is hidden from a reader. Separation upstream is by BRANCH, not by prefix — a header branch is a different CRDT with its own topic, which is why upstream needs no reserved prefix at all.
The urn: strings that appear upstream are unit-test fixtures and nothing else: urn:g, urn:child:dg, urn:child:sg inside #[cfg(test)] mod tests in engine/verifier/src/orm/graph/utils.rs, and urn:test:… under sdk/rust/src/tests/. They are not vocabulary and imply nothing.
What this package writes
urn:ng-eventually: — MACHINERY_NS (packages/polyfill/src/emulated-verifier/machinery.ts), in two families: urn:ng-eventually:shim:…, the compartments shared-wallet/account-registry.ts uses to emulate a repo's branches inside one document, and urn:ng-eventually:inbox:…, the deposit envelope and the deposit kinds emulated-verifier/inbox-dispatch.ts dispatches on.
Triples under it are dropped on this package's OWN read, and nowhere else. isMachinerySubject is called from readDocumentSubjects (packages/polyfill/src/surface/read-document.ts), which skips every row whose subject starts with the prefix. By subject only — a machinery predicate on an ordinary subject comes straight through. That read is INTERNAL since 1.0.0-dev.8: the published readUnion used to apply the same filter for an application, and with it gone an anchored docs.sparqlQuery returns the machinery rows and the application ignores them itself. No right moves with that: the only registers an ordinary document carries are its inbox address and, for a public document, the read key its store already serves to whoever asks.
That filtering has no upstream counterpart: it is an invention, and the source annotates it as one (kind=invention level=none). It exists because this package has no branches, so a compartment upstream keeps in a separate CRDT has to live as a reserved subject in the same graph. Do not read our reservation as mirroring anything upstream — nothing there is filtered out of a document's own graph.
The index arm writes another package's namespace. urn:ng-helpers:index:field and urn:ng-helpers:index:value (packages/polyfill/src/emulated-verifier/index-deposit.ts) are the IRIs @ng-helpers/indexing already writes, kept letter for letter because renaming them would orphan every index already written. It is odd for this package to write a namespace it does not own. Recorded here as an oddity; it is not this leaf's to fix.
Two kinds of triple, two fates — the distinction this leaf exists for
Scaffolding is what this package writes to emulate what the engine will do: the branch compartments, the deposit envelope. It is transitory. It disappears at migration together with the read filter that hides it, and being invisible to a caller is exactly right for it. urn:ng-eventually: is its home.
Durable application data is what an application means: an index entry written into a public document, read by third parties, which must survive migration. It is deliberately not under urn:ng-eventually: — which is precisely why the index IRIs pass the read filter and reach the caller.
Naming them alike would say the wrong thing about both: it would promise the scaffolding a future it does not have, and it would put the durable data one widened filter away from disappearing. The test at every new IRI: does this triple die at migration, or does it have to outlive it? Scaffolding goes under urn:ng-eventually:; anything an application or a third party reads must not. This is the triple-level twin of knowledge_what-an-app-deletes-at-migration — there, the two fates of a published symbol; here, the two fates of a written triple.
What is settled, and what is a bet
Settled, read at the source. Nothing upstream reserves, filters or validates a namespace; the engine stores and returns arbitrary IRIs; there is no extension convention to comply with and no procedure for claiming a segment. Nothing upstream binds our choice, and nothing upstream breaks if we keep a urn: scheme.
A bet. That upstream will not later claim urn: or begin separating by prefix. Nothing says it will; nothing says it will not — and the absence of an implementation says nothing about what the target will do (the no-divergence rule (.project/rules/nextgraph/no-divergence-from-nextgraph.md)). The exposure is small and it falls on the scaffolding side, which is the half designed to disappear.
A bet, and the more expensive one. Writing urn:ng-helpers: from here. Two packages now share one data format with no owner and no agreement about who may change it.
Not a bet — a divergence, if it were ever done. Putting our own terms under did:ng:x:…. That segment is the target's vocabulary space, did:ng:x:ng# is the engine's own, and x means nothing to the address grammar — so borrowing it would dress an invention in the target's vocabulary, which the no-divergence rule (.project/rules/nextgraph/no-divergence-from-nextgraph.md) names as the most dangerous kind of all.