Skip to content

Serve the AAS projection over content negotiation - #72

Open
LKSNDRTMLKV wants to merge 4 commits into
mainfrom
feat/repin-core-0-14
Open

Serve the AAS projection over content negotiation#72
LKSNDRTMLKV wants to merge 4 commits into
mainfrom
feat/repin-core-0-14

Conversation

@LKSNDRTMLKV

Copy link
Copy Markdown
Member

Closes #69.

GET /dpp/{id} with Accept: application/aas+json now returns an IDTA Asset Administration Shell Environment. This is the first time the AAS projection has been reachable over HTTP — it has existed in the core library for several releases with no consumer at all, and this repo did not even depend on the crate that holds it.

Three commits, in the order they must land.

1. Repin to dpp-core 0.14.1

Port traits dispatch on the sector's catalog key rather than the Sector enum, so PluginHost and ComplianceRegistry implementations take &str and call sites pass Sector::catalog_key(). Taking the enum meant a plugin could only ever be loaded for a sector this build already had a variant for.

The lockfile also moves wasmtime 46.0.1 → 46.0.2, clearing RUSTSEC-2026-0222 and RUSTSEC-2026-0223 — low-severity advisories on the Wasm sandbox's internal state handling. Unrelated to the repin; the advisory database updated.

Also fixes .cargo/config.toml.example, which never gained dpp-vc or dpp-aas after they were split out upstream. Anyone using the local-core override was silently getting those two from the registry and everything else from the working tree.

2. Record the core version in the evidence dossier

A dossier named only the node version, so it attested a compliance determination without naming the code that computed it. The regulatory logic, schemas and disclosure policy behind a verdict live in the core library, and the two version lines move independently.

coreVersion is also reported on /api/v1/info and /health.

Breaking: the manifest uses deny_unknown_fields, so this is a format change rather than an optional addition. No dossier exists outside tests, so format "1" is defined to include it rather than carrying an optional field permanently.

3. The AAS door

Built from the verified signed public payload, never the live row — the same discipline the JSON-LD door uses, so body and signature agree by construction. An Environment assembled from current database state would drift from the view the operator actually signed.

Field selection is not made in this repo. The door calls dpp_aas::build_aas_environment, which filters the passport through the disclosure seam before any mapper sees it. A projection that picked its own field list would eventually disagree with the canonical one, and the direction it disagrees in is the direction that leaks.

Tests

  • The gate: none of battery's eight restricted or individual-tier fields appears in the public output, asserted by name. Battery because it has the most non-public fields of any product group.
  • The projection is not empty — a masking test that passed because nothing was emitted would be worthless.
  • 406 for a media type this route cannot produce.
  • */*, application/json and an absent Accept still reach the JSON-LD default.
  • A fixture-validity guard: without it, an invalid fixture would make the masking assertion pass vacuously against a 502.

Two judgement calls

The 406 is deliberately narrow. Only a header naming something genuinely unproducible gets it. An absent, empty, */*, application/*, application/json or application/ld+json header all still reach the JSON-LD default — RFC 9110 §12.5.1, and also what curl and most HTTP clients send. A broader reading would have broken every existing consumer of this route.

A passport with no GTIN returns 406. Unsold-goods reports and untyped sectors carry none — they do not identify a trade item — so no AAS asset identity exists for them. That is more honest than an Environment with an invented globalAssetId.

Scoped out

AAS reads are counted as the json scan variant. Telling them apart from JSON-LD reads needs a migration, since the variant column is CHECK-constrained, and is only worth doing if the distinction is ever needed.

Verification

just check green against the published 0.14.1 crates, with no local-core override present: 591 tests.

Port traits now dispatch on the sector's catalog key rather than the Sector enum, so a plugin can be loaded for a sector this build has no variant for; the lockfile also moves wasmtime to 46.0.2 for two sandbox-state advisories.
An evidence dossier named only the node version, so it attested a compliance determination without naming the code that computed it — the regulatory logic behind a verdict lives in the core library, and the two version lines move independently.
The AAS projection has existed in the core library for several releases with no HTTP door; it is built from the verified signed payload and masked before any mapper sees it, so the new projection cannot widen what a public caller receives.
@codacy-production

codacy-production Bot commented Jul 31, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 41 complexity · 12 duplication

Metric Results
Complexity 41
Duplication 12

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@LKSNDRTMLKV

Copy link
Copy Markdown
Member Author

Pushed feat(resolver): complete the AAS door's contract (e43a195), which closes the gaps between what this PR's description claims and what it tested.

This PR now depends on odal-node/dpp-core#86 and cannot merge before it. That change fixes four defects that made the AAS Environment invalid against IDTA's published schema — pinned at 0.14.1, this door serves documents no AAS parser would accept. The repin needs to move to the release carrying those fixes, and the wording added to api/openapi.yaml about schema validation is only true afterwards.

What the new commit adds:

  • The signed-payload path is now tested. Every AAS test here ran with verification disabled, so none of them could tell the signed payload from the served row — the PR's central safety claim was asserted only in prose. There is now a test that signs one product name, serves another, and requires the projection to show what was signed.
  • A cross-door gate. Nothing compared the two representations. Each had its own masking test, which is the shape that drifts: both keep passing while the two definitions of "public" separate. Any key the JSON-LD door drops must now be absent from the AAS projection, driven from the served passport rather than a hand-written list. I verified it fails by planting a leak before trusting it.
  • The battery fixture carries all ten of its non-public fields, up from three. Five of the eight assertions in the existing masking test named fields the fixture never carried and could not have failed.
  • Link: <…>; rel="alternate"; type="application/ld+json" on every 200. The unsigned half of the sealed decision shipped; the link half did not. alternate rather than canonical because the two representations share one URL and are separated only by Accept, so canonical would point the resource at itself. Errors carry no Link.
  • Determinism is asserted, not assumed.
  • Accept weights are honoured. application/aas+json;q=0 was served AAS — the one representation the client asked not to receive. This route now agrees with dpp-digital-link's parser, which always honoured q.
  • text/* resolves to HTML instead of 406; its sibling application/* was already honoured. Narrow by design — it selects HTML only when nothing JSON-shaped is also acceptable, so text/*, application/json still returns JSON-LD.
  • OpenAPI documents the AAS media type, the 406, the Link header, and the schema-valid-not-conformance-certified wording.

One caveat on coverage: the cache-hit path is not driven end-to-end. Both success paths now return through a single constructor, and that constructor is unit-tested, but populating a real cache hit needs Redis and the resolver has no integration tier. The test says so rather than implying more coverage than it has.

just check green: 602 tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Serve the AAS projection over content negotiation

1 participant