Skip to content

Sync and async publish canonicalize root subjects differently #1122

Description

@zsculac

This was generated by AI during triage.

Summary

Sync publish and async publish appear to use different root/subject canonicalization semantics. That makes the same domain payload produce different RDF subjects depending on whether it goes through agent.publish() or agent.publishAsync(), which breaks stable IRI linking and graph visibility assumptions for integrations.

The async lift path rewrites requested roots to generated canonical roots:

  • packages/publisher/src/async-lift-validation.ts:38-47 builds canonicalRootMap and canonicalizes public/private quads.
  • packages/publisher/src/async-lift-validation.ts:73-98 rewrites matching subjects and object IRIs.
  • packages/publisher/src/async-lift-validation.ts:101-105 generates roots like dkg:<cg>:<namespace>:<scope>/<name>-<hash>.
  • packages/agent/src/dkg-agent-publish.ts:1054-1091 uses the validated/canonicalized async payload when building the async lift seal.

Sync publish, by contrast, takes the caller-provided quads/JSON-LD IDs into _publish() and then through the normal canonical publish payload path without this async-lift root rewrite:

  • packages/agent/src/dkg-agent-publish.ts:1172-1283 passes caller quads/privateQuads to publisher.publish().
  • packages/publisher/src/canonical-publish-payload.ts canonicalizes the publish payload for hashing, but does not generate the async-lift dkg:<cg>:<namespace>:<scope>/... subject IRIs.

Why this matters

For domain integrations, caller-provided IRIs are the graph model. Example shape:

  • tenant root: urn:dmaast:tenant:tenant-a
  • device root: urn:dmaast:device:tenant-a:device-a
  • source root: urn:dmaast:data-source:tenant-a:device-a:source-a
  • device links to tenant by IRI
  • source links to tenant and device by IRI

With sync publish, these stable IRIs remain visible as the subjects/objects and the VM graph renders connected.

With async publish, the lift validator rewrites roots to generated dkg:.../<name>-<hash> roots. That means the same input no longer has the same external root identity across sync and async paths. It can also split graph links and make downstream lookups harder unless the caller knows and follows the async-generated canonical root mapping.

Expected behavior

Sync and async publish should have consistent root/subject semantics for identical inputs, or the SDK/API should expose a clear compatibility contract.

Possible fixes:

  1. Preserve caller-provided root IRIs in async publish, matching sync publish.
  2. If async needs generated DKG roots internally, persist explicit alias/equivalence metadata and make query/UI/link traversal resolve source IRIs and canonical IRIs consistently.
  3. Provide an opt-in/opt-out option for async root canonicalization, with the default matching sync publish for JSON-LD @id payloads.

Suggested acceptance tests

  • Publish the same JSON-LD hierarchy through sync and async:
    • Tenant has stable @id.
    • Device has stable @id and links to tenant by IRI.
    • Data source has stable @id and links to both tenant and device by IRI.
  • The finalized VM graph has the same root entity semantics in both paths.
  • The UI graph renders connected tenant -> device -> source clusters in both paths.
  • SPARQL lookups by caller-provided root IRI work in both paths, or a documented alias query works equivalently.
  • UAL/root metadata exposes enough mapping for downstream integrations to fetch by the original domain IRI.

Observed downstream workaround

The DMAAST Kafka endpoint registration integration switched to sync publish by default because it needed stable tenant/device/data-source IRIs and visible connected graph clusters for the demo.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpre-mainnetMust land before mainnet launchpriority:highMust-fix: protocol correctness, security, economics, or headline flow broken

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions