npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@metamynd/mmt-graph

v0.19.0

Published

Reference trust-graph engine (Oxigraph/SPARQL + SHACL) + HCS-anchor contract: apply mutation events, validate the MMTO §3 authority-chain invariant, batch the changelog into a Merkle root ready to anchor, and verify a disclosed tenant graph with MetaMynd

Readme

@metamynd/mmt-graph — reference trust-graph engine

The MMTO Phase 1/2 reference implementation: apply mutation events to a validated RDF graph, and batch the resulting changelog into a Merkle root ready to anchor. See docs/design/metamynd-trust-ontology.md Appendix C for the full phased roadmap this package works through.

No Postgres reads/writes anywhere in this package, no MetaMynd backend required. The MetaMynd-operated hosting mode wires this in as a best-effort, opt-in shadow-write (backend/src/features/trust-graph/mmt-graph.adapter.ts, gated by MMT_GRAPH_ENABLED, default off) — but that's one caller among many this package is designed to have, not a dependency of it. npm test makes no live Hedera calls either — the one exception is mmt-graph.anchor-live-demo.mjs, a manually-run, not-yet-executed script proving the anchor contract against real testnet infrastructure (see "Status" below).

Self-hosting quickstart (Phase 4)

This package is designed to run identically whether MetaMynd operates it or you do — same engine, same event vocabulary, same anchor contract, no MetaMynd credentials or MetaMynd backend anywhere in the code path. That split was a Phase 1 design constraint, not a later retrofit: mmt-graph.anchor.mjs never imports @hashgraph/sdk, and nothing in this package imports a database driver.

npm install @metamynd/mmt-graph
node node_modules/@metamynd/mmt-graph/mmt-graph.self-host-quickstart.mjs

That script runs the whole lifecycle end to end, offline, with zero setup: register your agents/principals/mandates, validate (SHACL + the §3 authority-chain invariant), query (it's a real SPARQL 1.1 graph, not a fixed set of report endpoints), batch the event log into a Merkle root, anchor it (a stub submit by default), and independently verify the result — the same replay-and-check the Phase 6 auditor runs for a third party, run here for yourself. Read the file; every step is commented with what a real deployment does differently.

To anchor for real, replace the quickstart's stub submit with your own Hedera client — mmt-graph.anchor-live-demo.mjs is the complete, real worked example (topic creation, transaction signing, receipt handling) against your own operator account. anchorBatch(batch, submit) is the entire seam: submit takes the canonical batch-root message and returns {transactionId, status}; nothing else in the package needs to know Hedera exists. Point it at your own topic (a fresh one, or MMT_GRAPH_TOPIC_ID if you already have one) and your own testnet or mainnet account — this package has no opinion on which.

What self-hosting does NOT give you today: a way to get your EXISTING historical Policy/AgentIdentity/Principal rows into the graph (no backfill mechanism exists in this package or the MetaMynd adapter — only new writes since you start applying events are ever recorded), and no durability layer of its own (Oxigraph's JS binding is in-memory only; your own event log — however you choose to persist it — is the real source of truth, and replay() is how you rebuild the graph from it on restart). Both are real gaps, not hidden ones; see Appendix C of the ontology doc for the intended direction.

Once you have events flowing and a batch anchored, a third party (a regulator, an auditor, you) can verify your tenant's graph state with MetaMynd fully offline — see mmt-graph.auditor.mjs/mmt-graph.auditor-cli.mjs below.

Files, in the order the phases that produced them shipped

  • mmt-graph.spike.mjs (Phase 1 spike, PR #382) — the storage-engine decision record. Proved Oxigraph (RDF/SPARQL, in-process WASM) + rdf-validate-shacl works before the real engine was built. Kuzu was disqualified (archived October 2025 after Apple acquired the company behind it); Apache AGE was viable but a Postgres C-extension distribution burden with no SHACL; CozoDB was genuinely close (native persistence, time-travel queries) but Datalog rather than RDF/SPARQL, and pre-1.0 with no storage-compatibility promise. Oxigraph's JS binding is in-memory only — not disqualifying, because the changelog below is the durable source of truth and the store is a materialized view rebuilt from it, not a workaround.
  • mmt-graph.types.mjs, mmt-graph.project.mjs, mmt-graph.shapes.ttl, mmt-graph.engine.mjs (Phase 1 engine, PR #383) — the real thing. MmtGraphEngine applies {op, ...} mutation events (agentRegistered/principalRegistered/ mandateGranted/mandateRevoked, grounded 1:1 against real Policy/AgentIdentity/ Principal rows — see the doc's domains A/C), projects them to RDF quads, and validate() runs the SHACL shapes plus a SPARQL ASK making the §3 authority-chain invariant runnable for every agent with a mandate, root or delegated. replay() rebuilds from the event log — the "materialized view from a durable log" story made real.
  • mmt-graph.merkle.mjs, mmt-graph.changelog.mjs, mmt-graph.anchor.mjs (Phase 2, PR #384) — turns MmtGraphEngine#log into an anchor-ready Merkle batch, and defines the anchor contract. mmt-graph.anchor.mjs never imports @hashgraph/sdk and makes no network call: anchorBatch(batch, submit) takes an injected submit(message) → {transactionId, status} function, so the actual Hedera client/credentials/topic management stay the hosting operator's problem — a MetaMynd backend wiring in something shaped like backend/src/features/trust/trust.anchor.ts, or a self-hosted tenant wiring in their own. That split is what keeps this package identical across both hosting modes. Fixed post-Phase-5: mmt-graph.changelog.mjs's EVENT_LEAF_FIELDS — the explicit, by-hand field list each event's Merkle leaf is built from — drifted out of sync as Phase 5's six slices added fields to existing ops and six wholly new ops, with nothing that actually anchors those events to catch it. agent-registered.principalDid, principal-registered's verification/rep fields, and mandate-granted.expiresAt were silently EXCLUDED from their own leaf hash (not a crash — worse: a batch could commit to a leaf that omits a real field); the six new ops would have thrown out of eventLeaf entirely. Caught while scoping Phase 6 (a public verifier can't honestly claim to check a tenant's full event log while half the vocabulary is unhashable), fixed with a completeness test that constructs one of every real event op and asserts eventLeaf accepts it, plus a regression test proving each previously-dropped field now actually changes the leaf.
  • mmt-graph.anchor-live-demo.mjs — a real, working submit wired against Hedera testnet, using @hashgraph/sdk as a devDependency (never imported by mmt-graph.anchor.mjs itself). Written and offline-test-verified, not yet actually run against testnet — see "Status: pending first live run" below.
  • mmt-graph.evidence-path.mjs (Phase 5, first slice) — proves domain H (runtime activity/evidence) can answer evidence-path.ts's core question — "can a third party verify this decision without us?" — as a graph query. New event types (decisionRecorded/evidenceRecorded/batchAnchored) and their projectors, plus isIndependentlyVerifiable(engine, decisionId), which mirrors the real function's exact precedence rule (a batch's anchor status wins over the evidence event's own status when a batch exists — the FILTER NOT EXISTS in the SPARQL query is what enforces that, tested explicitly). Not wired to any live endpointMMT_GRAPH_ENABLED is off by default, and even on, nothing backfills historical decisions into the graph yet, so there's no way today for this to answer for the population of existing decisions the way trust-graph.repository.ts's live Postgres queries do. blast-radius, contagion and accountability, initially deferred pending an Issuer concept, verified SPARQL aggregate support and a Human/person concept respectively, are each covered separately — see mmt-graph.contagion.mjs, mmt-graph.accountability.mjs and mmt-graph.blast-radius.mjs below.
  • mmt-graph.contagion.mjs (Phase 5, second and fourth slices) — proves domain F (governance) can answer contagion.ts's hard question: not "what does this agent share with others" but "which shares are SPECIFIC enough to mean something," as a SPARQL COUNT/GROUP BY aggregate query (confirmed Oxigraph supports this before building on it). isSpecific() is ported verbatim from contagion.ts — same constants (BROAD_COHORT_MIN = 10, BROAD_COHORT_SHARE = 0.5), same two-condition logic — and tested against the real measured platform-standard case (isSpecific(57, 92) === false), built as an actual 92-agent graph, not just the isolated function call. A nice convergence with Phase 3's tenant-isolation fix (PR #387): the tenantAgents denominator isSpecific needs no longer has to be passed in externally — each MmtGraphEngine instance already IS one tenant's data, so counting mmt:AIAgent nodes in it gives the right number for free. All four real cohort kinds are covered (sop/standard from the second slice, organization/unit from the fourth) even though the real algorithm computes them via two different code shapes — the aggregate query doesn't need to know that, because mmt:sharesCohort is emitted uniformly for all four. organization/unit additionally assert the real, already-speced Domain A relationship mmt:memberOf (Agent → mmt:Organization/mmt:AgentGroup, both named in the doc, neither implemented until now) — deliberately two predicates, not one, because sharing a SOP and belonging to an organization are different kinds of fact and blurring them for query convenience would undo a distinction the ontology already drew (tested explicitly: organization/unit assert mmt:memberOf, sop/standard do not).
  • mmt-graph.accountability.mjs (Phase 5, fifth slice) — proves domain A (actors) can answer accountability.ts's core question, "who is ultimately answerable for this decision?", for its load-bearing core: decision -> agent -> organization -> human, and the four gap codes that determine answerable itself (agent-unresolved, no-organization, organization-unverified, no-authorized-representative). Closes the mmt:Human gap the first slice's investigation flagged — humans get a synthetic IRI (no DID; the User table has none), reached via mmt:operatedBy (Organization → Human, domain A's own relationship list, never implemented until now). principalIsVerified is ported verbatim from backend/src/features/principal/principal.verification.ts, and the tests use accountability.test.ts's exact fixture values (org-1/agent-1/user-rep/ "Ada Rep"), including its lapsed-KYB case. Deliberately NOT attempted, the same kind of scope split as contagion's two slices: the operating-unit hop (unit-org-mismatch), the delegation-root distinction (the mandate's authorising principal, when it differs from the agent's home organisation), and human review/approval linkage (review-unlinked) — real parts of accountability.ts's full output shape, but refinements on top of the answerable/not-answerable determination this slice targets.
  • mmt-graph.blast-radius.mjs (Phase 5, sixth and final slice) — answers blast-radius.ts's actual question, not the one originally posed. "Which agents hold credentials from this issuer" cannot be walked at all — issued VCs are signed and HCS-anchored but never persisted, per that file's own header — so it answers what the real code was rewritten to answer instead: an issuer's tier derives from its backing principal, so what depends on that principal, and which LIVE mandates would start getting blocked with PRINCIPAL_UNVERIFIED if its verification lapsed (blockedOnLapse is a hypothetical count, not gated on whether verification is currently in force — mirrored exactly, not conflated with mmt-graph.accountability.mjs's principalIsVerified). mmt:Issuer is the one genuinely new class this package adds (the W3C VC Data Model's own term, grounded in issuer.model.ts's real did), plus a new mmt:backedBy (Issuer → Organization, adopted from the real edge-kind name, same precedent as mmt:decidedFor). Everything else reuses vocabulary already built: mmt:memberOf is exactly "agents whose accountable organisation is this principal," and the previously-unimplemented mmt:grantsMandate (named since Phase 0, Domain C) gives authorizedByPrincipal the per-mandate precision mmt:actsFor alone can't (it lives on the agent node, not the mandate). Tested against blast-radius.test.ts's real fixture values (iss-1/p-1/Acme Holdings). All four Platform Governance §7 questions now have a graph-query proof — none wired to a live endpoint; see "Not in scope here" below for why.
  • mmt-graph.auditor.mjs, mmt-graph.auditor-cli.mjs (Phase 6) — the public verification tooling: same zero-MetaMynd-server posture as integrations/magp-evidence's auditor.mjs/magp-evidence.mjs (node:crypto + fetch only), extended for what a graph can disclose that a flat evidence leaf can't. Two shapes: { event, proof, root } mirrors magp-evidence exactly (reproduce the leaf, verify the Merkle proof, optionally confirm on-chain anchoring against a public Hedera mirror node); { events: [...], root } REPLAYS a whole disclosed log through a fresh MmtGraphEngine and checks the recomputed root against what was claimed AND that the replayed graph itself validates — the same SHACL shapes and §3 authority-chain invariant validate() runs internally, now run by a third party who never trusted MetaMynd to have run it correctly the first time. CLI: npm run auditor -- <disclosure.json> [--topic 0.0.x] [--network testnet|mainnet], same flags and VALID/INVALID verdict shape as magp-evidence's auditor.mjs. Fixed post-Phase-5, before building this on top of it: mmt-graph.changelog.mjs's EVENT_LEAF_FIELDS — the explicit field list each event's Merkle leaf is built from — had drifted out of sync as Phase 5's six slices extended event schemas. Three existing ops (agent-registered, principal-registered, mandate-granted) had new fields silently EXCLUDED from their own leaf hash (not a crash — worse, a batch could commit to a leaf that omits a real field); six new ops had no field list at all and would have thrown out of eventLeaf. Caught with impact() showing zero real callers (nothing anchors Phase 5's proof-only events yet), fixed with a completeness test (one of every real event op, asserts eventLeaf accepts it) and a regression test proving each previously-dropped field now actually changes the leaf.

Vocabulary note

The spike originally used mmt:actsFor for Mandate→Agent. Domain A of the MMTO draft defines mmt:actsFor as Agent→Principal ("the Principal is the actor whose authority the agent exercises"), and the source proposal's own worked example uses a separate mmt:grantedTo for Mandate→Agent. Fixed in both the spike and the engine when the inconsistency was found while building Phase 1 — see mmt-graph.project.mjs's header.

Status: pending first live run

mmt-graph.anchor-live-demo.mjs is code-complete and reuses the same dev testnet credentials backend/.env already has configured (HEDERA_OPERATOR_ID/ HEDERA_OPERATOR_KEY/HEDERA_NETWORK=testnet — the same ones trust.anchor.ts uses) — but it has never actually been executed. Running it was blocked by Claude Code's own auto-mode safety classifier (an external-network-with-credentials action), and the call was made to commit the working code rather than force it through. Whoever picks this up next should run it once and record the result here:

cd integrations/mmt-graph
MMT_GRAPH_DOTENV_PATH="../../backend/.env" node mmt-graph.anchor-live-demo.mjs

Expect it to print a topic ID, a transaction ID, and a mirror-node URL. If it works, this line should be replaced with the actual transaction ID and a ✅. If it doesn't, that's the first real signal about what Phase 3's live wiring will actually need to handle.

What a real submit looks like

The live demo script above is the working version. A MetaMynd-backend-shaped equivalent would follow trust.anchor.ts's pattern instead of raw @hashgraph/sdk calls:

async function submit(message) {
  const client = getHederaClient(); // backend/src/features/hedera/hedera.client.js
  const topicId = await resolveOrCreateMmtGraphTopic(); // this tenant's own topic, Postgres-cached
  const tx = new TopicMessageSubmitTransaction().setTopicId(topicId).setMessage(message);
  const resp = await tx.execute(client.getClient());
  const receipt = await resp.getReceipt(client.getClient());
  return { transactionId: resp.transactionId.toString(), status: receipt.status.toString() };
}

Run it

npm install
npm run spike              # the Phase 1 decision record
npm test                   # engine + merkle + changelog + anchor-contract + evidence-path + contagion + accountability + blast-radius + auditor, all offline
npm run anchor-live-demo   # NOT part of npm test — needs real testnet credentials, see above
npm run auditor -- disclosure.json [--topic 0.0.x] [--network testnet|mainnet]   # Phase 6's public verifier

Not in scope here

No backend wiring, no new DB tables, no Postgres access anywhere in this package (including the live demo — topic resolution there is env-var based, mirroring trust.anchor.ts's TRUST_TOPIC_ID fallback, not its DB-backed cache). npm test makes no live Hedera/HCS calls — mmt-graph.anchor.test.mjs checks this by inspecting mmt-graph.anchor.mjs's actual imports, not just by claiming it in prose; the live demo script and the auditor's own mirror-node read (Phase 6, tested via an injected fetchImpl, never a real call in npm test) are the only two exceptions. All four of Platform Governance §7's questions now have a graph-query proof (Phase 5): evidence-path, contagion (all four cohort kinds), accountability (its load-bearing core — see mmt-graph.accountability.mjs's header for what's deliberately out of scope: the operating-unit hop, the delegation-root distinction, human review linkage), and blast-radius (see mmt-graph.blast-radius.mjs's header — answers the question the real code was rewritten to ask, not the un-walkable original). None are wired to a live endpoint — that cutover needs a real backfill/durability design, not attempted anywhere in this package.