Skip to content

fix(schema): move schema $id off a domain we do not own - #42

Merged
imran-siddique merged 1 commit into
mainfrom
fix/schema-id-domain
Sep 2, 2026
Merged

fix(schema): move schema $id off a domain we do not own#42
imran-siddique merged 1 commit into
mainfrom
fix/schema-id-domain

Conversation

@imran-siddique

Copy link
Copy Markdown
Member

The problem

All eight schemas, in all three copies, identify themselves under https://agentrust.io:

"$id": "https://agentrust.io/telemetry/v0.1/schema/envelope.schema.json"

That domain is not ours. It serves 114 bytes:

<!DOCTYPE html><html><head><script>window.onload=function(){window.location.href="/lander"}</script></head></html>

A parked-domain lander. It also answers HTTP 200 for every path, so anything resolving a schema $id gets HTML with a success code rather than a 404 it could act on. Our real site is agentrust-io.com, served from GitHub Pages.

Before this PR, agentrust-io.com appeared in zero files in the repository.

This is the same class of defect trace-spec corrected in its v0.1 to v0.2 cutover, where the concern was letting "a record minted under a domain we do not own continue to pass as conformant."

The change

The $id host in spec/schema/, src/agentrust_telemetry/schemas/ and packages/typescript/schemas/, plus the id template in validation.ts. One line per file, 25 files.

Relative $refs are untouched and still resolve, because swapping the host keeps the base hierarchical.

Why this needs no hosting change to be correct

Neither SDK dereferences $id. Both read the schemas from local files and register them in their validator by $id, so nothing depends on the URL being fetchable. Serving the eight files at that path is worth doing and is not a prerequisite for this to be right. Even unserved, an identifier on a domain we control is strictly better than one on a parked domain that returns 200 and HTML.

Considered and rejected: a tag URI

tag:agentrust-io.com,2026:telemetry/... would have matched the identifier style trace-spec v0.2 uses. It does not work here, tested both ways:

  • Python (referencing): relative $refs cannot resolve against a non-hierarchical tag: base. Making all cross-schema refs absolute does fix it, and passes 6/6 valid and 7/7 invalid fixtures.
  • Ajv, used by the TypeScript SDK: cannot resolve tag: URIs at all, even fully absolute ones. can't resolve reference tag:agentrust-io.com,2026:telemetry/v0.1/schema/common.schema.json#/$defs/event_id

So a tag URI would break the TypeScript SDK. trace-spec uses one as a claim value (eat_profile), not as a JSON Schema $id with refs hanging off it, which is why the parallel does not carry.

Verification

Gate Result
check_versions / check_schemas / check_typescript_schemas / check_otel_compatibility pass
Conformance fixtures 13/13
Python tests 108 pass, 2 skipped
TypeScript tests 41 pass
examples/governed_workflow.py pass
Residual bare agentrust.io none

Branched from main, so it is independent of #41 and the two can land in either order.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Xyu1wLe68MPCKaqUXeEpXn

All eight schemas, in all three copies, identified themselves under
https://agentrust.io. That domain is not ours. It serves a 114 byte parked
lander, `window.location.href="/lander"`, and it answers HTTP 200 for every
path, so a resolver fetching a schema $id gets HTML and a success code rather
than a 404 it could act on. The real site is agentrust-io.com, on GitHub Pages.

This is the same class of defect trace-spec corrected in its v0.1 to v0.2
cutover, where a v0.1 identifier would have let a record minted under a domain
we do not own keep passing as conformant.

Changes the $id host in spec/schema, src/agentrust_telemetry/schemas and
packages/typescript/schemas, plus the id template in validation.ts. Relative
$refs are untouched and still resolve, because the host swap keeps the base
hierarchical.

Neither SDK dereferences $id. Both load schemas locally and register them by
$id, so nothing depends on the URL being fetchable and this needs no hosting
change to be correct. Serving the eight files at that path is worth doing and
is not a prerequisite.

Considered and rejected: tag:agentrust-io.com,2026:telemetry/... to match
trace-spec's EAT profile URI. Python needs every cross-schema $ref rewritten
absolute for that to resolve at all, and Ajv cannot resolve tag: URIs even when
they are absolute, so it would break the TypeScript SDK. trace-spec uses a tag
URI as a claim value, not as a JSON Schema $id with refs hanging off it.

Gates: check_versions, check_schemas, check_typescript_schemas,
check_otel_compatibility, 13/13 conformance fixtures, 108 Python tests,
41 TypeScript tests, governed_workflow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xyu1wLe68MPCKaqUXeEpXn
@imran-siddique
imran-siddique merged commit b687a6b into main Sep 2, 2026
8 checks passed
@imran-siddique
imran-siddique deleted the fix/schema-id-domain branch September 2, 2026 20:09
imran-siddique added a commit that referenced this pull request Sep 2, 2026
Release preparation for `0.1.0-alpha.2`, following step 1 and 2 of
`RELEASING.md`.

## What is in this release

All three already merged to `main`:

| PR | |
|---|---|
| #41 | wire `spec_version` now matches `spec/VERSION` |
| #42 | schema `$id` moved off `agentrust.io`, a domain we do not own |
| #43 | npm dist-tag derived from `spec/VERSION` |

## Why a new version instead of releasing v0.1.0-alpha.1

npm already holds `0.1.0-alpha.1` from the bootstrap publish that
`RELEASING.md` explicitly sanctions:

> If npm does not expose publisher settings until the first version
exists, bootstrap only that first package ownership using npm's
interactive 2FA flow

`release.yml` publishes both registries from one release event, and
`release-assets` has `needs: [publish-pypi, publish-npm]`. Cutting
`v0.1.0-alpha.1` would publish PyPI, fail `publish-npm` on the duplicate
version, and skip attestation entirely. A fresh version lets one build
feed both registries with provenance intact, which is what the pipeline
exists to do.

PyPI has never been published, so `0.1.0-alpha.2` will be its first
version.

## On the moved digests

`spec/VERSION` is the contract version and the source both package
versions derive from, so bumping it changes the wire `spec_version`,
which sits inside the RFC 8785 bytes that get hashed. Both goldens were
regenerated from the code rather than hand-edited, and Python and
TypeScript independently agree on the new tool-transcript hash.

## A dead test case, found while bumping

`tests/test_repository_gates.py` had `"0.1.0-alpha.1"` as a dict key
**twice**:

```python
"0.1.0-alpha.1": ("0.1.0.dev0", "0.1.0-alpha.1.0"),
"0.1.0-alpha.1": ("0.1.0a1", "0.1.0-alpha.1"),
```

Python keeps the last, so the first entry was silently discarded and the
`dev` phase has never been tested. Its npm spelling was wrong as well:
`0.1.0-alpha.1.0` is not something `ecosystem_versions` can produce for
any input. Restored as a real `0.1.0-dev` case, with both alpha
spellings now asserted.

## Verification

| Gate | Result |
|---|---|
| `check_versions` | `contract=0.1.0-alpha.2 python=0.1.0a2
npm=0.1.0-alpha.2` |
| `check_release_tag v0.1.0-alpha.2` | pass |
| `npm_dist_tag` | `alpha` |
| `check_schemas` / `check_typescript_schemas` /
`check_otel_compatibility` | pass |
| Conformance fixtures | 13/13 |
| Python tests | 111 pass |
| TypeScript tests | 41 pass |

## After this merges

1. Configure the npm trusted publisher for `@agentrust-io/telemetry`,
now possible because the package exists.
2. Create the GitHub release tagged `v0.1.0-alpha.2` targeting `main`.
3. Approve the `pypi` and `npm` deployment jobs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01Xyu1wLe68MPCKaqUXeEpXn

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant