70 lines
9.4 KiB
Markdown
70 lines
9.4 KiB
Markdown
---
|
|
type: knowledge
|
|
summary: 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
|
|
last_checked: 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 as `did: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 read.** `isMachinerySubject` is called from `readUnion` (`packages/polyfill/src/surface/read-model.ts`), which skips every row whose **subject** starts with the prefix before the caller ever sees it. By subject only — a machinery predicate on an ordinary subject comes straight through.
|
|
|
|
**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* ([[rule_no-divergence-from-nextgraph]]). 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 [[rule_no-divergence-from-nextgraph]] names as the most dangerous kind of all.
|