@flashyos/directory
v0.2.0
Published
The estate directory — one record per real thing, emitted as fragments by each repository and merged into a single world model every property renders a view over.
Maintainers
Readme
@flashyos/directory
One record per real thing, emitted by whoever is the authority for it.
The estate directory. Every repository emits a fragment — the nodes and edges it owns — and the fragments merge into one graph that every property renders a view over. Nothing is copied between repositories, so nothing can drift.
Why fragments
A central seed script that reaches into seventeen checkouts works on one laptop and fails on every runner. A repository knows what it owns; nothing else reliably does. So authority is federated and merging is where the estate-wide rules get enforced.
The thirteen kinds
org person agent prop place event std cred claim inst
txn work src
And fourteen edge types, of which owns is the cap table and publishes
records who stands behind a standard.
An id is prefix/slug, permanent, and never reissued — not even after a
property is retired and its domain lapses.
Emitting a fragment
import { emitter } from '@flashyos/directory'
const e = emitter({ source: 'repo/gda-group', assertedBy: 'person/michael' })
e.node('Person', 'michael', 'Michael')
e.node('Organization', 'gda-capital', 'GDA Capital', { platform: 'capital' })
e.node('Property', 'gda.group', 'gda.group', { tenure: 'freehold' })
e.edge('accountableFor', 'person/michael', 'org/gda-capital')
e.edge('operates', 'org/gda-capital', 'prop/gda.group')
e.edge('holds', 'org/gda-capital', 'place/decentraland/fashion-street',
{ qty: 116, unit: 'parcel', since: '2021-11' })
writeFileSync('directory.fragment.json', JSON.stringify(e.build(), null, 2))Provenance is added automatically. A record whose asserter and expiry are set by hand is a record that will eventually be missing both.
Merging
import { merge, errors, freshness } from '@flashyos/directory'
const { graph, problems } = merge(fragments)
if (errors(problems).length) process.exit(1)
console.log(`freshness ${(freshness(graph) * 100).toFixed(1)}%`)What it refuses
| Refuses | Because |
|---|---|
| Two repositories emitting the same id | Exactly one repository is the authority for a record. |
| Two properties defines the same claim | One claim, one canonical home. |
| An edge pointing at a node nobody emitted | A property would render a link to nothing. |
| An organisation with no accountableFor | An unowned organisation is a liability with a renewal fee. |
| A record with no asserter | An unattributed fact cannot be assessed or safely inherited. |
| An expiry before its assertion | Every fact has a review date, and it is in the future. |
| owns with no percentage or instrument | An ownership edge without those is a rumour. |
Decay
Every assertion carries asserted and expires. Past expiry a fact does not
become false and is never deleted — it stops claiming to be current, everywhere
it renders, at once. freshness() returns the share of assertions still inside
their review window.
That number, not the record count, says whether the directory is an asset or a liability. It cannot be improved by adding records, which is the point.
Visibility
public · partner · private. visibleTo(graph, audience) filters nodes
and every edge touching them, so a private record returns exactly what a
missing one returns. Existence is itself organisation data.
Ownership edges default to private. Publishing one raises a warning, so a
position is only ever disclosed deliberately.
Addressing
An identifier is permanent under Rule 05. resolveOrganization says where it
goes:
import { resolveOrganization, organizationDocumentUri } from '@flashyos/directory'
resolveOrganization('org/flashyos', 'flashyos.com')
// → 'https://flashyos.com/organizations/flashyos' the identity URI
organizationDocumentUri('flashyos.com', 'flashyos')
// → 'https://flashyos.com/organizations/flashyos.json' the documentThe identity URI is extension-free and version-free, because an organisation is
not a JSON file and an identifier containing /v1/ is a promise to change at
v2. A host answers it with 303 See Other pointing at the document.
The host is an argument, and that is the whole design. There is no lookup
here and no default. Rule 11 puts a fact at its asserter's own host, so this
function cannot be called without having already learned — from a document that
named it — whose fact this is. A version that knew a canonical host for
org/acme would be a registry, and would hand whoever ran it the power to
decide what other companies are called.
A host serves records only for organisations it asserts. For the rest it answers
404 and names where they do resolve. See SPEC §3.4.
Licence
Apache-2.0. A registry one company controls is a database; a registry anyone can implement is a standard.
